原型链
基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
// 继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
};
var inst = new SubType();
alert(inst.getSuperValue());
借用构造函数
这种技术的基本思想是:在子类型构造函数的内部调用父类型的构造函数。函数是在特定环境中执行代码的对象。
function SuperType(name) {
this.name = name;
this.age = 22;
}
function SubType(name) {
// 继承了SuperType
SuperType.call(this, name);
}
var instance1 = new SubType('paper_crane');
instance1.age = 25;
alert(instance1.name); // paper_crane
alert(instance1.age); // 25
var instance2 = new SubType('crane');
alert(instance2.name); // crane
alert(instance2.age); // 22
缺点:在父类型中定义的函数,子类型无法使用;子类型只能通过在构造函数里面定义函数,那么就无法实现函数复用。
组合继承
组合继承也叫伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
alert(this.name);
};
function SubType(name, age) {
// 继承了SuperType
SuperType.call(this, name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
alert(this.age);
};
var instance1 = new SubType('paper_crane', 22);
instance1.colors.push('black');
alert(instance1.colors); // red,blue,green,black
instance1.sayName(); // paper_crane
instance1.sayAge(); // 22
var instance2 = new SubType('crane', 18);
alert(instance2.colors); // red,blue,green
instance2.sayName(); // crane
instance2.sayAge(); // 18
优点:JavaScript最常用的继承模式,instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。缺点:子类的实例有colors这个数组,原型也有colors这个数组,会重复,只需delete instance1.colors即可验证;同时,每次创建子类对象都会调用两次父类构造函数。
原型式继承
思想是先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个构造函数的一个新实例。
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
var person = {
name: 'paper_crane',
friends: ['zhangsan', 'lisi', 'wangwu']
};
var anotherPerson = object(person);
anotherPerson.name = 'crane';
anotherPerson.friends.push('qianliu');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = 'paper';
yetAnotherPerson.friends.push('zhaoqi');
alert(anotherPerson.name); // crane
alert(anotherPerson.friends); // zhangsan,lisi,wangwu,qianliu,zhaoqi
alert(yetAnotherPerson.name); // paper
alert(yetAnotherPerson.friends); // zhangsan,lisi,wangwu,qianliu,zhaoqi
缺点:对象person里面的friends数组对于每个子类来说是共享的,原因是friends是引用类型,只保留一个指向数组对象的指针,每创建一个新的子类时候,相当于复制了一份指针指向的地址。
ES5通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和一个为新对象定义额外属性的对象(此参数可选)。当只有一个参数时,和上面定义的object()函数功能一样。
寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert('hi');
};
return clone;
}
var person = {
name : 'paper_crane',
friends: ['zhangsan', 'lisi', 'wangwu']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // hi
寄生组合继承
组合继承无论在什么情况下都会调用两次父类型的构造函数:一次是在创建子类型的原型的时候,一次是在子类型构造函数内部。而所谓的寄生组合式继承能够避免这种缺点,其通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
alert(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
alert(this.age);
};
var instance1 = new SubType('paper_crane', 22);
var instance2 = new SubType('crane', 18);
instance1.sayName();
instance1.sayAge();
instance2.sayName();
instance2.sayAge();
优点:避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链保持不变,能够正常使用instanceof和isPrototypeOf()。