繼承
許多
OO
語言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承占键。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法尉辑。由于函數(shù)沒有簽名鸠补,在ECMAScript
中無法實(shí)現(xiàn)接口繼承萝风,ECMAScript
只支持實(shí)現(xiàn)繼承,而且其實(shí)實(shí)現(xiàn)繼承的主要是依靠原型鏈來實(shí)現(xiàn)的紫岩。
一、原型鏈
基本思想是利用原型讓一個(gè)引用型繼承另一個(gè)引用類型的屬性和方法睬塌。
構(gòu)造函數(shù)泉蝌、原型和實(shí)例的關(guān)系:
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對象歇万,原型對象包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例包含一個(gè)指向原型對象的內(nèi)部指針勋陪。
// 構(gòu)造函數(shù) SuperType
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
// 構(gòu)造函數(shù) SubType
function SubType() {
this.subproperty = false;
}
// 繼承了 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue());
那么贪磺,讓原型對象等于另一個(gè)類型的實(shí)例,此時(shí)的原型對象將包含一個(gè)指向另一個(gè)原型的指針诅愚,相應(yīng)地寒锚,另一個(gè)原型中也包含一個(gè)指向另一個(gè)原型的指針。假如另一個(gè)原型又是另一個(gè)原型的實(shí)例违孝,那么上述關(guān)系依然成立刹前,如此層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條雌桑。這就是所謂原型鏈的基本概念喇喉。
-
確定原型和實(shí)例的關(guān)系
a.
instanceof
b.
isPrototypeOf()
-
原型鏈的問題
在通過原型來實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會變成另一個(gè)類型的實(shí)例校坑。于是拣技,原先的實(shí)例屬性也就順理成章地變成了現(xiàn)在的原型屬性。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {}
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"
3.1.SubType
通過原型鏈繼承了SuperType
之后耍目,SubType.protype
就變成了SubperType
的一個(gè)實(shí)例膏斤,因此它也擁有了一個(gè)它自己的colors
屬性,就跟專門創(chuàng)建了一個(gè)SubType.prototype.colors
屬性一樣邪驮。結(jié)果SubType
的所有實(shí)例都會共享這一個(gè)colors
屬性莫辨。
3.2 創(chuàng)建子類型的實(shí)例時(shí),不能想超類型的構(gòu)造函數(shù)傳遞參數(shù)耕捞。不能在不影響所有對象類型的情況下衔掸,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。
二俺抽、借用構(gòu)造函數(shù)
這種也稱為偽造對象或經(jīng)典繼承敞映,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
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"
2.1 傳遞參數(shù)
function SuperType(name) {
this.name = name;
}
function SubType() {
SuperType.call(this, "Nicholas");
this.age = 29;
}
var instance = new SubType();
alert(instance.name);
alert(instance.age);
2.2 構(gòu)造函數(shù)的問題
方法都在構(gòu)造函數(shù)中定義磷斧,無法做到函數(shù)復(fù)用
三振愿、組合繼承
組合繼承,有時(shí)候也叫做偽經(jīng)典繼承弛饭,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊冕末,從而發(fā)揮二者之長的一種繼承模式。思路是使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承侣颂,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承档桃。
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.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
組合繼承是最常用的繼承模式。
四憔晒、原型式繼承
借助已有的對象創(chuàng)建新對象藻肄,同時(shí)還不必因此創(chuàng)建自定義類型蔑舞。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"],
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);
ECMAScript5
通過新增Object.create()
方法規(guī)范化了原型式繼承。這個(gè)方法接收兩個(gè)參數(shù):一個(gè)用作新對象原型的對象和一個(gè)為新對象定義額外屬性的對象嘹屯。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"],
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);
五攻询、寄生式繼承
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù)州弟,該函數(shù)在內(nèi)部以某種方式來增強(qiáng)對象钧栖,最后再像真地是它做了所有工作一樣返回對象。
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: "Nicholas",
friends: ["Shelby", "Court", "Van"],
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();
不能做到函數(shù)復(fù)用婆翔;
六拯杠、寄生組合繼承
組合繼承最大問題是無論什么情況下,都會調(diào)用兩次超類型構(gòu)造函數(shù):一次是創(chuàng)建子類型的時(shí)候浙滤,另一次是在子類型構(gòu)造函數(shù)內(nèi)部阴挣。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType) {
// 創(chuàng)建對象
var prototype = object(superType.prototype);
// 增強(qiáng)對象
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);
};
只調(diào)用了一次SuperType
構(gòu)造函數(shù),最理想的繼承方式