摘要:當(dāng)調(diào)用的構(gòu)造函數(shù)時,在函數(shù)內(nèi)部又會調(diào)用的構(gòu)造函數(shù),又在新對象上創(chuàng)建了實例屬性和,于是這兩個屬性就屏蔽了原型中的同名屬性。
前言:這次對上篇收個尾,主要總結(jié)一下javascript的繼承。
1.原型鏈js中原型鏈?zhǔn)菍崿F(xiàn)繼承的主要方法。基本思想是:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。我們來簡單回顧一下以前的內(nèi)容:
每個構(gòu)造函數(shù)都有一個原型對象
每個原型對象都包含一個指向構(gòu)造函數(shù)的指針:(constructor)
而實例和構(gòu)造函數(shù)都有一個prototype屬性指針指向原型對象。
假如現(xiàn)在我們讓原型對象(A)等于另一個類型的實例(b),此時相當(dāng)于這個原型對象(A)整體作為一個實例指向另一個實例的原型對象(b的原型對象B)。
以上就實現(xiàn)了繼承。
看下面代碼實例:
function SuperType(){ this.property = true; //property是SuperType的實例屬性 }; SuperType.prototype.getSuperValue = function(){ //getSuperValue是SuperType的原型方法 return this.property; }; function SubType(){ this.subproperty = false; } //讓SuperType繼承SubType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
在上面的代碼中,定義了兩個類型:SuperType和SubType。每個類型分別有一個屬性和方法。
通過創(chuàng)建SuperType的實例,并賦值給了SubType.prototype,從而實現(xiàn)SubType繼承了這個的實例,
原來存在于SuperType的實例中的所有的屬性和方法,現(xiàn)在也存在于SubType.prototype中了。
既然現(xiàn)在SubType的原型對象SubType.prototype是SuperType的實例化對象,那么SuperType的實例屬性property就位于SubType.prototype。如下圖:
現(xiàn)在instance.constructor現(xiàn)在指向的是SuperType,圖中可以看出來。也可以在進行繼承之后,再進行如下步驟:
SubType.prototype.constructor = Subtype;
所有函數(shù)的默認(rèn)原型都是Object的實例,所以下圖是上面例子的完整原型鏈。
(1)重寫和添加方法必須在用超類型的實例(new SuperType())替換原型(SubType.prototype)之后。
function SuperType(){ this.property = true; }; SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //讓SuperType繼承SubType SubType.prototype = new SuperType(); //添加新方法 SubType.prototype.getSubValue = function(){ return this.subproperty; }; //重寫超類型中的方法 SubType.prototype.getSuperValue = function(){ return false; }; var instance = new SubType(); alert(instance.getSuperValue()); //false alert((new SuperType()).getSuperValue()); //我仿照java這么寫,居然返回true
重寫超類型中的方法之后,通過SuperType的實例調(diào)用getSuperValue()時,調(diào)用的就是這個重新定義的方法。
通過SuperType的實例調(diào)用getSuperValue()時,調(diào)用的就是超類型中的方法,返回true
(2)通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法,這樣會重寫原型鏈
function SuperType(){ this.property = true; }; SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //讓SuperType繼承SubType SubType.prototype = new SuperType(); SubType.prototype = { getSubValue: function(){ return this.subproperty; }, someOtherMethod: function(){ return false; } }; var instance = new SubType(); alert(instance.getSuperValue()); //error
(3)原型鏈的問題
在通過原型鏈進行繼承時,原型實際上會變成另一個類型的實例,所以原先的實例屬性也就變成了現(xiàn)在的原型屬性了。
現(xiàn)在假如原型實例的屬性是引用類型的,那么它會直接被添加成現(xiàn)在的對象原型的屬性,那么通過這個創(chuàng)建的實例對這個引用類型的屬性進行更改時,會立即反映在所有的實例對象上。
看下面代碼:
function SuperType(){ this.colors = ["red","blue","green"]; }; function SubType(){ } //讓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"]
當(dāng)SubType通過原型鏈繼承了SuperType之后,SubType.prototype就變成了SuperType的一個實例
此時SubType擁有一個自己的colors屬性,就像專門創(chuàng)建了一個SubType.prototype.colors屬性一樣
此時SubType所有的實例話對象都會共享這個colors屬性,修改instances1的colors屬性會立即在instances2中顯示出來。
原型鏈還有一個問題:在創(chuàng)建子類型的實例時,不能向超類型的構(gòu)造函數(shù)傳遞參數(shù),實際上是沒有辦法在不影響所有對象實例的情況下,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。
4.實現(xiàn)繼承的其它方法 (1)借用構(gòu)造函數(shù)基本思想:
在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型的構(gòu)造函數(shù),通過使用call()方法或者apply()方法。
例子:
function SuperType(name){ this.name = name; this.colors = ["red","blue","green"]; } function SubType(name,age){ //繼承了SuperType,同時還傳遞了參數(shù) SuperType.call(this,name); //再為子類型定義屬性 this.age = age; } var instance1 = new SubType("Jack"); alert(instance1.name); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
上述代碼中解決了一個問題,就是引用類型的屬性問題,每個實例化的子類型都有自己的特有的屬性
還存在一個問題,如果方法都定義在構(gòu)造函數(shù)中,那么方法的就不能復(fù)用。
(2)組合繼承-最常用的繼承模式組合繼承的思路是:
使用原型鏈實現(xiàn)對原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承
這樣既通過在原型上定義方法實現(xiàn)了函數(shù)復(fù)用,又能夠保證每個實例都有它自己的屬性。
例子:
function SuperType(name){ this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name,age){ //繼承SuperType的屬性 SuperType.call(this,name); this.age = age; } //繼承SuperType的方法 SubType.prototype = new SuperType(); //定義子類型自己的方法 SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Jack",26); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //Jack instance1.sayAge(); //26 var instance2 = new SubType("Rose",23); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //Rose instance2.sayAge(); //23(3)原型式繼承
思路:借助原型可以基于已有的對象創(chuàng)建新對象,還不必因此創(chuàng)建自己的自定義類型
如下:
function object(o){ function F(){}; F.prototype = o; return new F(); }
在object()函數(shù)內(nèi)部先創(chuàng)建一個臨時性的函數(shù)。
然后將傳入的對象作為這個構(gòu)造函數(shù)的原型。
最后返回這個臨時類型的餓新實例。
如下:
var person = { name:"Jack", friends:["路人甲","路人乙"] }; var anotherPerson = object(person); //此處調(diào)用上方的object方法 anotherPerson.name = "Rose"; anotherPerson.friends.push("路人丙"); var yetPerson = object(person); yetPerson.name = "Rick"; yetPerson.friends.push("路人丁"); alert(person.friends); //["路人甲","路人乙","路人丙","路人丁"]
上述person.friends不僅屬于person所有,而且會被anotherPerson和yetPerson共享。
還有Object.create()方法,前面已經(jīng)總結(jié)過了。
思路:創(chuàng)建一個僅用于封裝繼承過程的函數(shù)。
function createAnother(original){ var clone = object(original); //調(diào)用前面的object()方法 clone.sayHi = function(){ alert("hi"); }; return clone; } //使用 var person = { name:"Jack", friends:["路人甲","路人乙","路人丙"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"Hi"5.寄生組合式繼承 (1)組合繼承存在的問題
組合繼承是js最常用的繼承模式,不過它有自己的不足,組合繼承最大的問題在于要調(diào)用兩次超類型的構(gòu)造函數(shù),一次是創(chuàng)建超類型的實例賦值給子類型的原型對象時,一次是子類型構(gòu)造函數(shù)內(nèi)部。
最終子類型會包含超類型對象的全部實例屬性,但是我們不得不在調(diào)用子類型構(gòu)造函數(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); //第二次調(diào)用 this.age = age; } SubType.prototype = new SuperType(); //第一次調(diào)用 SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); };
第一次調(diào)用SuperType構(gòu)造函數(shù)時,SubType.prototype會得到兩個屬性:name和colors,它們都是SuperType的實例屬性,只不過位于SubType的原型中。
當(dāng)調(diào)用SubType的構(gòu)造函數(shù)時,在函數(shù)內(nèi)部又會調(diào)用SuperType的構(gòu)造函數(shù),又在新對象上創(chuàng)建了實例屬性name和colors,于是這兩個屬性就屏蔽了原型中的同名屬性。
(2)解決方法寄生組合式繼承的思想是:不必為了子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無非就是超類型的一個副本而已,本質(zhì)上就是使用寄生式繼承來繼承超類型的原型,把返回的結(jié)果賦值給子類型的原型。
大家一定還記得上面說的原型式繼承吧吧,將一個對象淺賦值給另一個對象,現(xiàn)在也可以把一個超類型的原型賦值給另一個子類型原型
1.回憶一下object()函數(shù)的代碼
function object(o){ function F(){} F.prototype = 0; return new F(); }
2.創(chuàng)建一個函數(shù),它接收兩個參數(shù):子類型構(gòu)造函數(shù)和超類型構(gòu)造函數(shù)。
function inheritPrototype(subType,superType){ var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; }
上面的代碼第一步創(chuàng)建超類型原型的一個副本
為創(chuàng)建的副本添加constructor屬性,彌補因重寫原型而失去默認(rèn)的constructor屬性
此處的重寫發(fā)生在object()函數(shù)里面,超類型的原型superType.prototype直接賦給了F.prototype,然后object()函數(shù)又返回了F的新實例。
把創(chuàng)建新的對象賦值給子類型的原型
3.那么現(xiàn)在來使用一下
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ù),因此避免了在SubType.prototype上面創(chuàng)建不必要的、多余的屬性,
此時原型鏈還能保持不變。
以上~~~~~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79980.html
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創(chuàng)建了一個具體的對象。對象就是數(shù)據(jù),對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:創(chuàng)建對象兩個基本方法創(chuàng)建對象最基本的兩個方法是構(gòu)造函數(shù)和對象字面量。當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新的實例對象后,該實例內(nèi)部會有一個指針指向構(gòu)造函數(shù)的原型對象。碼農(nóng)構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會返回新對象實例。 前言:本文主要總結(jié)一下javascript創(chuàng)建對象的方法、原型、原型鏈和繼承,但是先從創(chuàng)建對象的幾種方法開始,延伸到原型模式創(chuàng)建對象以及其它模式。繼承本來想一塊寫了,發(fā)現(xiàn)太多內(nèi)容了...
摘要:春招季如何橫掃面試核心考點基礎(chǔ)版前端面試之路二基礎(chǔ)整理的繼承和的繼承有什么區(qū)別的繼承時通過或構(gòu)造函數(shù)機制來實現(xiàn)。作用創(chuàng)建私有變量,減少全局變量,防止變量名污染。異步瀏覽器訪問服務(wù)器請求,用戶正常操作,瀏覽器后端進行請求。 春招季如何橫掃 Javascript 面試核心考點(基礎(chǔ)版)?前端面試之路二(javaScript基礎(chǔ)整理) ES5的繼承和ES6的繼承有什么區(qū)別 ES5的繼承時通過...
閱讀 3252·2021-10-21 17:50
閱讀 3262·2021-10-08 10:05
閱讀 3393·2021-09-22 15:04
閱讀 589·2019-08-30 14:00
閱讀 1949·2019-08-29 17:01
閱讀 1515·2019-08-29 15:16
閱讀 3225·2019-08-26 13:25
閱讀 858·2019-08-26 11:44