最近重温了《JavaScript 高级程序设计(第3版)》和学习了《Understanding ECMAScript 6》,趁此机会记录一下,加深一下自己的理解。
ECMAScript 5
不具备传统的面向对象所支持的类和接口等基本结构,使用引用类型(一种数据结构),描述一类对象所具有的属性和方法。
ECMAScript 6
具备传统的面向对象所支持的类和接口等基本结构。
下面具体讲解ECMAScript 5 的引用类型和 ECMAScript 6的类。
ECMAScript 5:
1. 对象:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。等同于:对象是一组没有特定顺序的值,对象每个属性或方法都有一个名字,每个名字都映射到一个值。
2. 创建对象:《JavaScript 高级程序设计(第3版)》书中提到三种模式,分别是工厂模式、构造函数模式、原型模式。
① 工厂模式:虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
function createPerson(name, age, job){
var o = new Object(); //创建新对象
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o; //将对象返回
}
var person1 = createPerson("Eve", 29, "software engineer");
var person2 = createPerson("Joy", 27, "Doctor");
② 构造函数模式:使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var person1 = new Person("Eve", 29, "software engineer");
var person2 = new Person("Joy", 27, "Doctor");
console.log(person1.constructor == Person); //true,表示是Person这一类人
console.log(person2.constructor == Person); //true,表示是Person这一类人
console.log(person1 instanceof Object); //true,表示是Object的实例
console.log(person1 instanceof Person); //true,表示是Person的实例
console.log(person2 instanceof Object); //true,表示是Object的实例
console.log(person2 instanceof Person); //true,表示是Person的实例
console.log(person1.sayName == person2.sayName); //false,表示不是同一个方法
需要注意的是:构造函数与其他函数的唯一区别,调用方式不同。任何函数只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数没有什么两样。
//当作构造函数使用
var person1 = new Person("Eve", 29, "software engineer");
var person2 = new Person("Joy", 27, "Doctor");
person1.sayName();
person2.sayName();
//作为普通函数调用
Person("Kim", 27, "Doctor"); //添加到window
window.sayName();
//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "happy", 25, "Teacher");
o.sayName();
③ 原型模式:无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。这个原型对象的用途是包含可以由特定类型的所有实例共享的属性和方法。这就意味着它同时解决了工厂模式的对象识别的问题和构造函数模式的方法共享问题。
function Person(){}
Person.prototype.name = "Eve";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
person1.sayName(); //Eve
var person2 = new Person();
person2.sayName(); //Eve
console.log(person1.sayName == person2.sayName); //true,表示同一个方法
构造函数、原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
Person的每一个实例——person1和person2都包含一个内部属性,该属性仅仅指向了Person.prototype; 即实例与构造函数没有直接的关系。读取对象属性时,搜索:对象实例本身(若没有则)->指针指向的原型对象。
需注意:重写原型对象需谨慎。
function Person(){}
var friend = new Person(); //先声明了实例
Person.prototype = { //再重写原型对象
constructor: Person,
name: "Eve",
age: 29,
job: "Software Engineer",
sayName: function() {
console.log(this.name);
}
}
friend.sayName(); //Uncaught TypeError: friend.sayName is not a function下图表示重写原型对象前后对比:
④ 组合使用构造函数模式和原型模式:较优。构造函数模式:定义实例属性;原型模式:定义方法和共享的属性。若只使用原型模式,当包含引用类型的属性时,如数组,实例会共享同一个数组,实例A操作数组B,会导致实例C中的数组B同时发生变化,在大数情况下,这是我们不愿意看到的情况。因此,结合构造函数模式,在构造函数中定义实例属性,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby","Court"];
}
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name);
}
}
var person1 = new Person("Eve", 29, "Software Engineer");
var person2 = new Person("Joy", 27, "Teacher");
person1.friends.push("Van");
console.log(person1.friends); //["Shelby", "Court", "Van"]
console.log(person2.friends); //["Shelby", "Court"]
console.log(person1.friends == person2.friends); //false
console.log(person1.sayName == person2.sayName); //true
持续更新中...欢迎交流~
参考资料:
1. JavaScript高级程序设计(第三版)--[美] Nicholas C.Zakas 著 李松峰 曹力 译
2. Understanding ECMAScript 6 (https://www.gitbook.com/book/oshotokill/understandinges6-simplified-chinese/details)
218

被折叠的 条评论
为什么被折叠?



