原型链继承
让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性。
优点:
写法方便简洁,容易理解。
缺点:
在父类型构造函数中定义的引用类型值的实例属性,会在子类型原型上变成原型属性被所有子类型实例所共享。同时在创建子类型的实例时,不能向超类型的构造函数中传递参数。
代码
// 原型链继承
function Parent() {
this.parentPrototype = "父级原型"
this.parentObj = {
info: "父级引用属性值info"
}
}
function child() { }
child.prototype = new Parent();
const a = new child()
console.log(a.parentPrototype)
const b = new child()
a.parentObj.info = "a里面的"
console.log(b.parentObj.info)
结果
借用构造函数继承
在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
优点:
解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
缺点:
借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
代码
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name); // 使用call将Parent构造函数绑定到Child上
this.age = age;
}
let child = new Child('Parent', 25);
console.log(child.name); // 输出 'Parent'
结果
组合继承:原型链 + 构造函数
将原型链和借用构造函数的组合到一块。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性。
优点:
解决了原型链继承和借用构造函数继承造成的影响。
缺点:
无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
代码
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 构造函数继承
this.age = age;
}
Child.prototype = new Parent(); // 原型链继承
let child = new Child('Parent', 25);
console.log(child.getName()); // 输出 'Parent'
结果
原型式继承
在一个函数A内部创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。本质上,函数A是对传入的对象执行了一次浅复制。ECMAScript 5通过增加Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的函数方法效果相同。
优点:
不需要单独创建构造函数。
缺点:
属性中包含的引用值始终会在相关对象间共享。
代码
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
let parent = {
name: 'Parent',
getName: function() {
return this.name;
}
};
let child = object(parent);
console.log(child.getName()); // 输出 'Parent'
结果
寄生式继承
寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。
优点:
写法简单,不需要单独创建构造函数。
缺点:
通过寄生式继承给对象添加函数会导致函数难以重用。
代码
function createAnother(original) {
let clone = object(original);
clone.sayHi = function() {
return 'Hi';
};
return clone;
}
let parent = {
name: 'Parent',
getName: function() {
return this.name;
}
};
let child = createAnother(parent);
console.log(child.sayHi()); // 输出 'Hi'
结果
寄生组合式继承 (寄生+组合(原型链+借用构造函数))
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
优点:
高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变。
缺点:
代码复杂。
代码
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function () {
console.log(this.name);
}
function Child(name) {
Parent.call(this, name);
}
inheritPrototype(Child, Parent);
let child1 = new Child('child1');
console.log(child1)
结果