6.3 继承
JS不支持接口继承,只支持实现继承,因为函数没有签名
原型链
原型链是继承的主要方法
每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针;
让原型对象等同于另一个类型的实例,这样原型对象将包含一个指向另一个原型的指针,相应地,另一个原型也包含着一个指向另一个构造函数的指针,如此层层递进,就构成了实例与原型的链条;
所有对象都继承object所以都拥有toString(),valueOf()这样的默认方法
instanceof和isPrototypeOf()可以判断原型与实例之间的关系
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
使用字面量添加新方法就会重写原型链
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;
},
someOtherMethod: function () {
return false;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!
原型链的问题
包含引用类型值的原型
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green,black"
在创建子类型的实例时,不能向超类型的构造函数中传递参数;
实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数;
基于这两点原因,实践中很少会单独使用原型链;
借用构造函数
也叫伪造对象或经典继承;
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
通过call()或apply()在新创建的实例的环境中调用了SuperType构造函数,这样每个SubType实例都有自己的colors副本了;
借用构造函数的问题
回顾6.2构造函数的问题,这里也一样,要么无法函数复用要么全是全局函数;
在超类型的原型中定义的方法对子类型是不可见的,所有类型都只能用构造函数模式;
基于这两点原因,实践中很少会单独使用借助构造函数;
组合继承
也叫伪经典继承;
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;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript 中最常用的继承模式;
而且,instanceof 和isPrototypeOf()也能够用于识别基于组合继承创建的对象;
但是,无论什么情况下,都会调用两次超类型构造函数:一次在创建子类型原型的时候,另一次在子类型构造函数内部;
原型式继承
举例
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样;
寄生式继承
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式;
示范继承模式时使用的object()函数不是必需的;
任何能够返回新对象的函数都适用于此模式;
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
寄生组合式继承
举例
function object(o) {
function F() {
}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType) {
var prototype = object(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);
};
拷贝一个超类型,增加constructor属性,赋值给子类型;
这样就避免了像组合继承一样调用两次超类型构造函数;
同时还能使用instanceof和isPrototypeOf();
是实现基于类型继承的最有效方式;