数天前在知乎看到有人阿里面试被问到这个问题,我来总结一下。
1. 原型链继承:
function SuperType() {
this.property = true;
} SuperType.prototype.getSuperProperty = function() {
return this.property;
}; function SubType() {} SubType.prototype = new SuperType(); var o = new Subtype(); console.log(o.getSuperProperty); // true
原型链的问题:子类所有对象的原型都是同一父类对象,如果我们修改到了原型对象,那么所有子类对象内容都会改变。
2. 借用构造函数(也称作伪造对象继承和经典继承):
借用构造函数是在子类中调用父类的构造函数(所谓借用)。
function SuperType() {
this.property = ['hello', 'world'];
} function SubType() {
SuperType.call(this);
}
构造函数的问题:父类在原型链中定义的函数不能被子类访问,也就是说所有的函数都必须写在构造函数内部,无法复用。
3. 组合继承(伪经典继承)*:
将原型链继承和借用构造函数继承组合到一起。
function SuperType(name) {
this.name = name;
this.color = ['red', 'green', 'blue'];
} SuperType.prototype.sayName = function() {
console.log(this.name);
}; function SubType(name) {
SuperType.call(this, name); // 第二次调用
} SubType.prototype = new SuperType(); // 第一次调用
SubType.prototype.constructor = Subtype; // 为了以后判断类型
不过组合继承也不是没有缺点,就是会至少调用两次构造函数,第一次调用在原型中添加了 SuperType 所拥有的属性,第二次调用的时候又覆盖了一次。这个开销其实是没有必要的。
4. 原型式继承:
Ojbect.create(prototypeObj);
这个方法会创建一个新的对象,新的对象的原型是 prototypeObj。
缺点和原型继承一样。
5. 寄生式继承:
核心思想在于在函数内部增强对象,返回一个新对象。
function createAnother(obj) {
var clone = Object.create(obj);
clone.sayHello = function() {
console.log('Hello');
};
return clone;
}
6. 寄生组合式继承:
完善组合式继承,不必为了指定子类型的原型而调用超类的构造函数,我们需要的无非就是超类的原型的一个副本(创造副本是为了子类上属性的修改不影响在原型链上的父类)而已,我们在一个函数里创建一个增强过的对象作为子类的原型(所谓寄生)。
function SuperType(name) {
this.name = name;
} SuperType.prototype.sayName = function() {
console.log(this.name);
}; function SubType(name) {
Super.call(this, name);
} function inheritPrototype(subType, superType) {
var prototype = Object(superType.prototype);
// 如果不像上面这么写,那么在子类添加方法会影响到父类,这不合理。
prototype.constructor = subType;
subType.prototype = prototype;
} inheritPrototype(SubType, SuperType);
这个没什么缺点,主要是写着稍微麻烦一点。