继上一篇文章说到的原型模式创造对象,主要缺点是存在多个实例共享数组,一般来说不同数据应该有自己的属性副本,这就是开发过程中不单独使用原型模式的原因。
这一篇主要讲讲继承,是学习红宝书继承一章做的笔记。
JS的继承主要是通过原型链实现的。
原型链
通过原型继承多个引用类型的属性和方法。
重温一下构造函数,原型和实例的关系:
每个构造函数(Test)都有一个原型对象(Test.prototype),原型有一个属性指回构造函数(constructor),而实例有一个内部指针(proto)指向原型。如果原型是另一个对象的实例,意味着这个原型本身有个内部指针指向另一个原型,另一个原型也有一个指针指向另一个构造函数。实例与原型之间构造了一条原型链。
SubType.prototype = new SuperType(); //重写原型
-
默认原型
- 默认所有引用类型都继承自object,也是通过原型链实现
- 这就是自定义类型能够继承toString等默认方法的原因
-
原型与继承关系
- instanceof 操作符
-
关于方法
- 子类覆盖父类的,需要等到原型赋值后再修改
- 以字面量创建原型方法会破坏之前的原型链
// 继承 SuperType SubType.prototype = new SuperType(); // 通过对象字面量添加新方法,这会导致上一行无效 SubType.prototype = { getSubValue() { return this.subproperty; }, someOtherMethod() { return false; } };
-
原型链的问题
-
在使用原型实现继承时,原型实际上变成另一个类型的实例,原先的实例属性会变成原型属性。
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() {} // 继承 SuperType SubType.prototype = new SuperType(); let instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" let instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green,black"
-
子类型实例化的时候不能传参
综上:原型链基本不会被单独使用
-
盗用构造函数
在子类构造函数中调用父类构造函数,可以使用call和apply方法创建对象得上下文执行构造函数。
主要缺点:必须在构造函数中定义方法,函数不能重用。此外子类也不能访问父类得方法。
function Child() {
//这里相当于在child对象上运行了parent所有的初始化的代码
Parent.call(this, "yaya");
}
let instance1 = new Parent();
- 传递参数
- 可以在子类构造函数中向父类构造函数传参
- 盗用构造函数问题
- 必须在构造函数中定义方法,因此函数不能重用。
- 子类也不能访问父类原型上定义的方法。
组合继承
结合构造函数和原型链
基本思路就是使用原型链继承原型上属性和方法,而通过盗用构造函数继承实例属性。
缺点:效率低,父类构造函数始终被调用两次。
function SubType(name, age){
// 继承属性
SuperType.call(this, name); //第二次调用
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType(); //第一次调用
寄生式继承
创建一个实现继承函数,以某种方式增强对象,然后返回这个对象。
function createAnother(original){
let clone = Object.create(original); // 通过调用函数创建一个新对象
clone.sayHi = function() { // 以某种方式增强这个对象
console.log("hi");
};
return clone; // 返回这个对象
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"
通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。
寄生组合继承
通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路就是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本,相当于寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
接收两个参数,子类构造函数 和父类构造函数,
1 取得父类原型副本
2 给返回的对象设置constructor,解决由于重写原型导致默认constructor丢失的问题
3 返回对象
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);
};