一.原型鏈###
原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法禀忆。每一個構(gòu)造函數(shù)都有一個原型對象百宇,原型對象都包含一個指向構(gòu)造函數(shù)的指針刻盐,而實例都包含一個指向原型對象的指針躬柬。如果:我們讓原型對象A等于另一個類型B的實例焦蘑,那么原型對象A就會有一個指針指向B的原型對象,相應(yīng)的B的原型對象中保存著指向其構(gòu)造函數(shù)的指針弥喉。假如B的原型對象又是另一個類型的實例郁竟,那么上述的關(guān)系依舊成立,如此層層遞進(jìn)由境,就構(gòu)成了實例與原型的鏈條
1.確定原型和實例的關(guān)系
通過兩種方式可以確定原型和實例之間的關(guān)系棚亩,第一種是使用instanceOf操作符,第二種是使用isPrototypeOf()方法虏杰。
實例 instanceOf 原型鏈 中出現(xiàn)過的構(gòu)造函數(shù)讥蟆,都會返回true
JavaScript中isPrototypeOf函數(shù)方法是返回一個布爾值,指出對象是否存在于另一個對象的原型鏈中纺阔。使用方法:object1.isPrototypeOf(object2)
2瘸彤、謹(jǐn)慎地定義方法
子類型有時候需要覆蓋超類型中的某個方法,或者需要添加超類型中不存在的莫個方法笛钝,注意:給原型添加方法的代碼一定要放在替換原型的語句之后质况。當(dāng)通過Child的實例調(diào)用getParentValue()時,調(diào)用的是這個重新定義過的方法玻靡,但是通過Parent的實例調(diào)用getParentValue()時结榄,調(diào)用的還是原來的方法。格外需要注意的是:必須要在Parent的實例替換原型之后囤捻,再定義這兩個方法潭陪。還有一點需要特別注意的是:通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法最蕾,因為這樣做會重寫原型鏈依溯。
3、原型鏈的問題
原型鏈很強大瘟则,可以利用它來實現(xiàn)繼承黎炉,但是也有一些問題,主要的問題還是包含引用類型值的原型屬性會被所有實例共享醋拧。因此我們在構(gòu)造函數(shù)中定義實例屬性慷嗜。但是在通過原型來實現(xiàn)繼承時,原型對象其實變成了另一個類型的實例丹壕。于是原先定義在構(gòu)造函數(shù)中的實例屬性變成了原型屬性了庆械。
二.借用構(gòu)造函數(shù)###
為了解決原型中包含引用類型值帶來的一些問題,引入了借用構(gòu)造函數(shù)的技術(shù)菌赖。這種技術(shù)的基礎(chǔ)思想是:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)缭乘。
Parent.call(this)在新創(chuàng)建的Child實例的環(huán)境下調(diào)用了Parent構(gòu)造函數(shù)。在新創(chuàng)建的Child實例環(huán)境下調(diào)用Parent構(gòu)造函數(shù)琉用。這樣堕绩,就在新的Child對象上策幼,此處的kid1和kid2對象上執(zhí)行Parent()函數(shù)中定義的對象初始化代碼。這樣奴紧,每個Child實例就都會具有自己的friends屬性的副本了特姐。
借用構(gòu)造函數(shù)的方式可以在子類型的構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)
為了確保子類型的熟悉不會被父類的構(gòu)造函數(shù)重寫,可以在調(diào)用父類構(gòu)造函數(shù)之后黍氮,再添加子類型的屬性唐含。
構(gòu)造函數(shù)的問題:
構(gòu)造函數(shù)模式的問題,在于方法都在構(gòu)造函數(shù)中定義沫浆,函數(shù)復(fù)用無從談起捷枯,因此,借用構(gòu)造函數(shù)的模式也很少單獨使用件缸。
三.組合繼承###
組合繼承指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合在一塊铜靶,從而發(fā)揮二者之長叔遂。即:使用原型鏈實現(xiàn)對原型屬性和方法的繼承他炊,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承。
Person構(gòu)造函數(shù)定義了兩個屬性:name和friends已艰。Person的原型定義了一個方法sayName()痊末。Child構(gòu)造函數(shù)在調(diào)用Parent構(gòu)造函數(shù)時,傳入了name參數(shù)哩掺,緊接著又定義了自己的屬性age凿叠。然后將Person的實例賦值給Child的原型,然后又在該原型上定義了方法sayAge().這樣嚼吞,兩個不同的Child實例既分別擁有自己的屬性,包括引用類型的屬性盒件,又可以使用相同的方法了。
組合繼承避免了原型鏈和構(gòu)造函數(shù)的缺陷舱禽,融合了他們的有點炒刁,成為JavaScript中最常用的繼承模式。而且誊稚,instanceOf和isPropertyOf()也能夠識別基于組合繼承創(chuàng)建的對象翔始。
四.對象冒充###
對象冒充包括三種:臨時屬性方式、call()及apply()方式
1.臨時屬性方式:
function A(x){
this.x=x;
this.say = function(){
alert('My name is '+this.name);
}
}
function B(x,y){
this.tmpObj=A;
this.tmpObj(x);
delete this.tmpObj;
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
var simon = new B('Simon',9527);
simon.say();
simon.showId();
/*第5里伯、6城瞎、7行:創(chuàng)建臨時屬性tmpObj引用構(gòu)造函數(shù)A,然后在B內(nèi)部執(zhí)行疾瓮,執(zhí)行完后刪除脖镀。當(dāng)在B內(nèi)部執(zhí)行了 this.x=x后(這里的this是B的對象),B當(dāng)然就擁有了x屬性狼电,當(dāng)然B的x屬性和A的x屬性兩者是獨立认然,所以并不能算嚴(yán)格的繼承补憾。第5、6卷员、7行有更簡單的實現(xiàn)盈匾,就是通過call(apply)方法:A.call(this,x);*/
2.call()/apply()方式:實質(zhì)上是改變了this指針的指向
function Person(name){
this.name = name;
this.say = function(){
alert('My name is '+this.name);
}
}
function F2E(name,id){
Person.call(this,name); //apply()方式改成Person.apply(this,[name]);
this.id = id;
this.showId = function(){
alert('Good morning,Sir,My work number is '+this.id);
}
}
var simon = new F2E('Simon',9527);
simon.say();
simon.showId();
/*call和apply都可以實現(xiàn)繼承,唯一的一點參數(shù)不同毕骡,func.call(func1,var1,var2,var3)對應(yīng)的apply寫法為:func.apply(func1,[var1,var2,var3])削饵。區(qū)別在于 call 的第二個參數(shù)可以是任意類型,而apply的第二個參數(shù)必須是數(shù)組未巫,也可以是arguments窿撬。*/
通過對象冒充的方式,無法繼承通過prototype方式定義的變量和方法:
五.寄生式繼承###
基本思想:創(chuàng)建一個僅用于封裝繼承過程的函數(shù)叙凡,該函數(shù)在內(nèi)部以某種方式來增強對象劈伴,最后再像真正是它做了所有工作一樣返回對象。
function createAnother(original) {
var clone = object(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
var person = {
name:"EvanChen",
friends:["Shelby","Court","Van"];
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();///"hi"