许多OO语言都支持两种继承方式,接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于在ECMAScript中,函数没有签名,无法实现接口继承,只支持实现继承,而且其实现继承主要是通过原型链来实现的。
一. 原型链模式
利用原型让引用类型继承另一个引用类型的属性和方法。
原型、构造函数和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针, 而实例包含一个指向原型对象的内部指针。
假如让原型对象等于另一个类型的实例,结果会如何?显然,此时的原型对象包好一个指向另一个原型的指针,相应地,另一个原型中也包含着指向另一个函数的指针。假如另一个原型又是另一个类型的实例,上述关系依然成立,如此层层递进,构成了实例与原型的链条。这就是所谓原型链的基本概念。
function SuperType() {
this.superProperty = true;
}
SuperType.prototype.getSuperProperty = function() {
console.log(this.superProperty);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubProperty = function() {
console.log(this.subProperty);
};
var instance = new SubType();
instance.getSuperProperty(); //true
this.superProperty = true;
}
SuperType.prototype.getSuperProperty = function() {
console.log(this.superProperty);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubProperty = function() {
console.log(this.subProperty);
};
var instance = new SubType();
instance.getSuperProperty(); //true
原型链模式的缺点:通过原型链实现继承时,原型实际上为另一个类型的实例,于是,原先的实例属性也变成了原型属性。而包含引用类型值的原型属性会被所有实例共享。
function SuperType() {
this.superProperty = true;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.getSuperProperty = function() {
console.log(this.superProperty);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubProperty = function() {
console.log(this.subProperty);
};
var instance1 = new SubType();
instance1.colors.push("purple");
console.log(instance1.colors); //["red", "blue", "green", "purple"]
var instance2 = new SubType();
console.log(instance2.colors); //["red", "blue", "green", "purple"]
this.superProperty = true;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.getSuperProperty = function() {
console.log(this.superProperty);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubProperty = function() {
console.log(this.subProperty);
};
var instance1 = new SubType();
instance1.colors.push("purple");
console.log(instance1.colors); //["red", "blue", "green", "purple"]
var instance2 = new SubType();
console.log(instance2.colors); //["red", "blue", "green", "purple"]
为了解决原型链模式引用类型属性继承的问题,接下来讨论借用构造函数模式。
二. 借用构造函数模式
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("purple");
console.log(instance1.colors); //["red", "blue", "green", "purple"]
var instance2 = new SubType();
console.log(instance2.colors); //["red", "blue", "green"]
this.colors = ["red", "blue", "green"];
}
function SubType() {
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("purple");
console.log(instance1.colors); //["red", "blue", "green", "purple"]
var instance2 = new SubType();
console.log(instance2.colors); //["red", "blue", "green"]
相对于原型链,借用构造函数模式中,子类型的构造函数能够向父类型的构造函数传递参数。
function SuberType(name) {
this.name = name;
}
function SubType(age) {
SuberType.call(this,"Mars");
this.age = age;
}
var instance = new SubType(27);
console.log(instance.name); //Mars
console.log(instance.age); //27
this.name = name;
}
function SubType(age) {
SuberType.call(this,"Mars");
this.age = age;
}
var instance = new SubType(27);
console.log(instance.name); //Mars
console.log(instance.age); //27
借用构造函数模式的缺点:仅是借用构造函数,无法避免构造函数模式存在的问题——方法都在构造函数中定义,函数复用无从谈起。为解决这个问题,接下来讨论组合继承模式。
三. 组合继承
组合继承:原型链模式+借用构造函数模式,原型链模式实现对原型属性和方法的继承,借用构造函数模式实现对实例属性的继承。
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);
this.age = age;
}
SubType.prototype = new SuperType();
var instance1 = new SubType("Brittany",23);
instance1.colors.push("black");
console.log(instance1.colors); //["red", "blue", "green", "black"]
console.log(instance1.sayName()); //Brittany
var instance2 = new SubType("Closure",24);
console.log(instance2.colors); //["red", "blue", "green"]
console.log(instance2.sayName()); //Closure
console.log(instance1 instanceof SubType); //true
console.log(instance1 instanceof SuperType); //true
console.log(SubType.prototype.isPrototypeOf(instance1)); //true
console.log(SuperType.prototype.isPrototypeOf(instance1)); //true
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
var instance1 = new SubType("Brittany",23);
instance1.colors.push("black");
console.log(instance1.colors); //["red", "blue", "green", "black"]
console.log(instance1.sayName()); //Brittany
var instance2 = new SubType("Closure",24);
console.log(instance2.colors); //["red", "blue", "green"]
console.log(instance2.sayName()); //Closure
console.log(instance1 instanceof SubType); //true
console.log(instance1 instanceof SuperType); //true
console.log(SubType.prototype.isPrototypeOf(instance1)); //true
console.log(SuperType.prototype.isPrototypeOf(instance1)); //true
时间:2014-10-22
地点:合肥
引用:《JavaScript高级程序设计》