继承
许多OOP语言都支持两种继承方式:接口继承和实现继承。由于ECMAScript中函数没有签名,故无法实现接口继承,只支持实现继承。ECMAScript中的的继承主要是通过原型链实现的。
1 原型链
利用原型让一个引用类型继承另一个引用类型的属性和方法。当我们让一个引用类型的原型等于另一个引用类型的实例,那么该引用类型的远新对象中将包含一个指向另一个原型的指针。以此可以构成原型链。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperTypeValue = function(){
return this.property;
}
function SubType(){
this.subProperty = fasle;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subProperty;
}
var instance = new SubType();
console.log(instance.getSuperTypeValue());//true
1.1 别忘了默认的原型
所有函数的默认原型都是Object的实例,因此默认原型内部会包含一个指针[[prototype]]指向Object.prototype。
1.2 确定原型和实例之间的关系
第一种方法:使用instanceof操作符。
console.log(instance instanceof SuperType); //true
console.log(instance instanceof SubType); //true
console.log(instance instanceof Object); //true
第二种方法:使用isPrototypeOf()方法。
console.log(SubType.prototype.isPrototypeOf(instance)); //true
console.log(SuperType.prototype.isPrototypeOf(instance)); //true
console.log(Object.prototype.isPrototypeOf(instance)); //true
1.3 谨慎地定义方法
给原型添加方法的代码一定要放在替换原型之后。
2 借用构造函数
有时候也叫伪造对象或经典继承。基本思想:在子类型构造函数的内部调用超类型构造函数。(函数只不过是在特定环境中执行代码的对象,可以使用call()或apply()在心间的对象上执行构造函数。
function SuperType(name){
this.name = name;
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
var instance = new SubType('Nicholas',29);
console.log(instance.name); //'Nicholas'
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,age);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new Subtype('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors); //['red','blue','green','black']
var instance2 = new SubType('Gred',26);
console.log(instance2.colors); //['red','blue','green']
4 原型式继承
即借助原型可以基于已有的对象创建新的对象,同时还不必因此创建自定义类型。在ECMAScript5中给出Object.create()方法实现,这个方法接受两个参数:一个用作新对象原型的对象,一个(可选的)为新对象定义的额外属性的对象。
var person = {
name:'Nicholas',
friends:['Cindy','Lily'];
}
var anotherPerson = Object.create(person,{
name:{
value:'Gred'
}
});
console.log(anotherPerson.name); //'Gred'
其中,Object.creat()方法的原理为:
function createObject(o){
function F(){};
F.prototype = o;
return new F();
}
5 寄生式继承
思路与工厂模式类似,即创建一个仅用于封装过程的函数,该函数在内部以某种方式来增强对象,最后再像真的做了所有工作一样返回对象。
function createAnother(original){
var clone = Object.create(original);
clone.sayHi = function(){
alert("hi");
}
return clone;
}
6 寄生组合式继承
组合继承最大的问题就是:无论在什么情况下,都会调用两次超类型构造函数。一次是在创建子类型的远行的时候,另一次是在子类型狗在函数内部。并且,在子类型原型的原型中有和实例属性重复的属性。
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,age); /*第二次调用SuperType()*/
this.age = age;
}
SubType.prototype = new SuperType(); /*第一次调用SuperType()*/
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new Subtype('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors); //['red','blue','green','black']
var instance2 = new SubType('Gred',26);
console.log(instance2.colors); //['red','blue','green']
解决方法:寄生组合继承
寄生组合继承就是通过借用构造函数来继承属性,通过原型链的混合形式来继承方法,基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们需要的无非就是超类型原型的一个副本而已。对于上述代码修改一句,如下:
SubType.prototype = Object.create(SuperType.prototype);
SubType.constructor = SubType;