愿意陪你笑的人很多,可愿意陪你哭的人却格外珍贵,能跟你共患难的人更是难得。
今日学习内容
1、JS 红皮书 P245-248 第八章:对象、类与面向对象编程
今日笔记
1、原型式继承: 他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。文章最终给出了一个函数:
1 2 3 4 5
| function object(o) { function F() {} F.prototype = o; return new F(); }
|
这个 object()函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上,object()是对传入的对象执行了一次浅复制。来看下面的例子:
1 2 3 4 5 6 7 8 9 10 11
| let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); let yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends);
|
ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的 object()方法效果相同:
1 2 3 4 5 6 7 8 9 10 11
| let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); let yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends);
|
Object.create()的第二个参数与 Object.defineProperties()的第二个参数一样:每个新增属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性。比如:
1 2 3 4 5 6 7 8 9 10
| let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person, { name: { value: "Greg" } }); console.log(anotherPerson.name);
|
2、寄生式继承: 寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。基本的寄生继承模式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function createAnother(original){ let clone = object(original); clone.sayHi = function() { console.log("hi"); }; return clone; }
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = createAnother(person); anotherPerson.sayHi();
|
3、寄生式组合继承: 组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。再来看一看这个组合继承的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); };
|
寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。寄生式组合继承的基本模式如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function inheritPrototype(subType, superType) { let prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); };
|
这里只调用了一次 SuperType 构造函数,避免了 SubType.prototype 上不必要也用不到的属性,因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此 instanceof 操作符和isPrototypeOf()方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。