繼承
在 ECMAScript 中由于函數(shù)沒有簽名卫漫,只支持實(shí)現(xiàn)繼承柑爸,依靠原型鏈來實(shí)現(xiàn)沛慢。
1.原型鏈
基本思想:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法服球。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 繼承了 SuperType 設(shè)置原型為超類的實(shí)例
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
// 調(diào)用到超類的方法
alert(instance.getSuperValue()); // true
以上的來個類型都分別有一個屬性和方法, SubType
繼承是通過創(chuàng)建 SuperType
的實(shí)例颠焦,將該實(shí)例賦給 SubType.prototype
實(shí)現(xiàn)斩熊,本質(zhì)是重寫原型對象。
1.1 別忘記默認(rèn)的原型
所有的引用類型默認(rèn)都繼承了 Object
伐庭,這個繼承也是通過原型鏈實(shí)現(xiàn)粉渠。所有函數(shù)的默認(rèn)原型都是 Object
的實(shí)例,因此默認(rèn)原型都會包含一個內(nèi)部指針([[prototype]]
或 __proto__
) 指向 Object.prototype
圾另。
1.2 確定原型和實(shí)例的關(guān)系
- 使用
instanceof
操作符霸株,測試實(shí)例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù),結(jié)果會返回true
集乔。
instance instanceof Object; // true
instance instanceof SuperType; // true
instance instanceof SubType; // true
- 使用
isPrototypeOf()
方法去件,只要是原型鏈中出現(xiàn)過的原型,也會返回true
扰路。
Object.prototype.isPrototypeOf(instance); // true
SuperType.prototype.isPrototypeOf(instance); // true
SubType.prototype.isPrototypeOf(instance); // true
1.3 謹(jǐn)慎地定義方法
給原型添加方法的代碼須放在替換原型的語句之后(否則方法是在默認(rèn)的原型上尤溜,替換后的原型是超類的實(shí)例)。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 繼承了 SuperType 替換原型實(shí)例
SubType.prototype = new SuperType();
// 添加了新方法(后置)
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
// 重寫超類型中的方法(后置)
SubType.prototype.getSuperValue = function() {
return this.false;
}
var instance = new SubType();
// 調(diào)用到超類的方法
alert(instance.getSuperValue()); // true
1.4 原型鏈的問題
包含引用類型值的原型屬性會被所有實(shí)例共享汗唱,因此需要在構(gòu)造函數(shù)而不是原型對象中定義屬性宫莱。
2. 借用構(gòu)造函數(shù)
在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù),同時通過使用 apply()
和 call()
方法也可以在(將來)新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)哩罪。
function SuperType() {
this.colors = ["red", "blue", "green"];
}
function SubType() {
// 繼承了 SuperType
SuperType.call(this);
}
var ins1 = new SubType();
ins1.colors.push("black");
alert(ins1.colors); // "red, blue, green, black"
var ins2 = new SubType();
alert(ins2.colors); // "red, blue, green"
2.1 傳遞參數(shù)
借用構(gòu)造函數(shù)可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)授霸。
function SuperType(name) {
this.name = name;
}
function SubType() {
// 繼承了 SuperType,同時傳遞了參數(shù)
SuperType.call(this, "Nicklas");
// 實(shí)例屬性
this.age = 29;
}
var ins = new SubType();
alert(ins.name); // "Nicklas"
alert(ins.age); // 29
2.2 借用構(gòu)造函數(shù)的問題
僅借用構(gòu)造函數(shù)际插,無法避免構(gòu)造函數(shù)模式的問題碘耳,即方法都在構(gòu)造函數(shù)中定義,無法實(shí)現(xiàn)復(fù)用函數(shù)框弛,同時辛辨,在超類型原型中定義的方法,對子類型也是不可見的功咒,因此構(gòu)造函數(shù)技術(shù)很少單獨(dú)使用愉阎。
3. 組合繼承
將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊绞蹦,思路是使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承力奋,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承,一方面通過原型上定義方法實(shí)現(xiàn)函數(shù)復(fù)用幽七,又能保證每個實(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 ins1 = new SubType("Nicklas", 29);
ins1.colors.push("black");
alert(ins1.colors); // "red, blue, green, black"
ins1.sayName(); // "Nicklas"
ins1.sayAge(); // 29
var ins2 = new SubType("Greg", 27);
alert(ins2.colors); // "red, blue, green"
ins2.sayName(); // "Greg"
ins2.sayAge(); // 27
4. 原型式繼承
借助原型可以基于已有的對象創(chuàng)建新對象,同時還不必因此創(chuàng)建自定義類型。
function object(o) {
function F() { }
F.prototype = o;
return new F();
}
在 object()
函數(shù)內(nèi)部猿挚,先創(chuàng)建一個臨時性的構(gòu)造函數(shù)咐旧,然后將傳入的對象作為這個構(gòu)造函數(shù)的原型,最后返回了這個臨時類型的一個新實(shí)例绩蜻。
5. 寄生式繼承
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似铣墨,即創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強(qiáng)對象办绝,最后再像真的是做了所有工作一樣返回對象伊约。
function createAnother(ori) {
var clone = object(ori); // 通過調(diào)用函數(shù)創(chuàng)建一個新對象
clone.sayHi = function() { // 以某種方式來增強(qiáng)這個對象
alert("hi");
};
retrun clone;
}
6. 寄生組合式繼承
組合繼承最大的問題是任何時候都會兩次調(diào)用超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類原型的時候,一次是在子類型構(gòu)造函數(shù)內(nèi)部孕蝉。
寄生組合式繼承即通過借用構(gòu)造函數(shù)來繼承屬性屡律,通過原型鏈的混成形式來繼承方法。不必為了制定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù)降淮,因?yàn)槲覀兯枰臒o非就是超類型原型的一個副本而已。本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型糊余,然后再將結(jié)果指定給子類型的原型棚赔。
function inheritPrototype(subType, superType) {
var protptype = object(superType.prototype); // 創(chuàng)建對象
prototype.constructor = subType; // 增強(qiáng)對象
subType.prototype = prototype; // 指定對象
}
后記
至此,第六章算是結(jié)束了系吩,但是整個過程有點(diǎn)痛苦繁成,因?yàn)閺睦斫鈱用鎭碇v,這一章的篇幅還是蠻大的淑玫,而且本書中這一章寫得太學(xué)術(shù)了巾腕,寫筆記也是挺累的,而且自認(rèn)為這三節(jié)筆記寫得質(zhì)量有點(diǎn)低絮蒿,但是一直跑不出這章太影響效率了尊搬,計(jì)劃在本書筆記完結(jié)后再來做一些簡略的補(bǔ)充和完善。