摘要:如前所述,中繼承是要分別繼承構(gòu)造函數(shù)和原型中的屬性和行為的。作為類的構(gòu)造函數(shù)存在,使用調(diào)用,例如作為類的構(gòu)造函數(shù),通過進(jìn)行類的實(shí)例化。
正統(tǒng)的面相對象的語言都會提供extend之類的方法用于出來類的繼承,但Javascript并不提供extend方法,在Javascript中使用繼承需要用點(diǎn)技巧。
Javascript中的實(shí)例的屬性和行為是由構(gòu)造函數(shù)和原型兩部分組成的,我們定義兩個類:Person和zhangsan,它們在內(nèi)存中的表現(xiàn)如下圖1:
如果想讓Zhangsan繼承Person,那么我們需要把Person構(gòu)造函數(shù)和原型中的屬性和行為全部傳給Zhangsan的構(gòu)造函數(shù)和原型,如下圖2所示:
Are you Ok?了解了繼承的思路后,那么我們一步步完成Person和Zhangsan的繼承功能。首先,我們需要定義Person類,如下代碼:
[代碼1]
// 定義Person類 function Person (name){ this.name = name; this.type = "人"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +",我的名字叫" + this.name); } } //定義Zhangsan類 function Zhangsan (name){ } Zhangsan.prototype={ }
Zhangsan雖然有自己特有的屬性和行為,但它大部分屬性和行為和Person相同,需要繼承自Person類。如前所述,JavaScript中繼承是要分別繼承構(gòu)造函數(shù)和原型中的屬性和行為的。我們先讓Zhangsan繼承Person的構(gòu)造函數(shù)中的行為和屬性,如下代碼:
[代碼2]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } //定義Zhangsan類 function Zhangsan (name){ this.name = name; this.type = "黃"; } Zhangsan.prototype={ } //實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); console.info(zs.type); // 黃
運(yùn)行正常,但我們怎么沒看到繼承的“味道”呢?我們在Zhangsan的構(gòu)造函數(shù)中將Person的屬性和行為復(fù)制了一份,與其說是繼承不如說是“真巧,這兩個類的構(gòu)造函數(shù)除了函數(shù)名不同,其他地方都長得一樣”。她的缺點(diǎn)很明顯:如果Person類的構(gòu)造函數(shù)有任何變動,我們也需要手動的同步修改Zhangsan類的構(gòu)造函數(shù),同樣一份代碼,我們復(fù)制了一份寫在了程序中 的不同地方,這違法了DRY原則,降低了代碼的可維護(hù)性。
好了,讓我們來改進(jìn)它:
[代碼3]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ Person(name); } Zhangsan.prototype={ } // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); console.info(zs.type); // undefined
我們在Zhangsan的構(gòu)造函數(shù)里調(diào)用Person()函數(shù),希望它內(nèi)部的ths.xxx可以在Zhangsan類的構(gòu)造函數(shù)里執(zhí)行一遍,但奇怪的是,出現(xiàn)“console.info(zs.type);”時,輸出的是undefined,這是怎么回事呢?
這和Person的調(diào)用方式有關(guān)。在JavaScript中,function有兩種不同的調(diào)用方法:
作為函數(shù)存在,直接用“()”調(diào)用,例如“function test(){}; test();”test被用作函數(shù),直接被“()”符號調(diào)用。
作為類的構(gòu)造函數(shù)存在,使用new調(diào)用,例如“function test(){}; new test();”test作為類的構(gòu)造函數(shù),通過new進(jìn)行test類的實(shí)例化。這兩種方法的調(diào)用,function內(nèi)部的this指向會有所不同---作為函數(shù)的function,其this指向的是window,而作為構(gòu)造函數(shù)的function,其this指向的實(shí)例對象。
上面代碼中,Zhangsan類構(gòu)造函數(shù)中的Person是通過函數(shù)方式調(diào)用的,它內(nèi)部的this指向的是window對象,起效果等同于如下代碼:
[代碼4]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ window.name = name; window.type = "黃"; } Zhangsan.prototype={ } // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); console.info(zs.type); // undefined console.info(type); // 黃 (window.type可以省略寫成type)
如果想達(dá)到[代碼3]的效果,讓Person內(nèi)部this指向Zhangsan類的實(shí)例,可以通過call或apply方法實(shí)現(xiàn),如下:
[代碼5]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype={ } // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); console.info(zs.type); // 黃
構(gòu)造函數(shù)的屬性和行為已經(jīng)成功實(shí)現(xiàn)了繼承,接下來我們要實(shí)現(xiàn)原型中的屬性和行為的繼承。既然Zhangsan類需要和Person類原型中同樣的屬性和行為,那么能否將Person類的原型直接傳給Zhangsan類的原型,如下代碼:
[代碼6]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = Person.prototype; // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); // 我是一個黃種人,我的名字叫張三 zs.say();
通過Person類的原型傳給Zhangsan類的原型,Zhangsan類成功獲得了say行為,但事情并不像想象中的那么簡單,如果我們要給Zhangsan類添加run行為呢?如下代碼:
[代碼7:添加run行為]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = Person.prototype; Zhangsan.prototype.run = function(){ console.info("我100米短跑只要10秒!"); } // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); zs.say(); // 我是一個黃種人,我的名字叫張三 zs.run(); //我100米短跑只要10秒! var zs2 = new Person("張三2"); zs2.run(); //我100米短跑只要10秒!
我們只想給Zhangsan類添加run行為,為什么Person類也獲得了run行為了呢?這涉及傳值和傳址的兩個問題----在JavaScript中,賦值語句會用傳值和傳地址兩種不同的方式進(jìn)行賦值,如果是數(shù)值型、不爾型、字符型等基本數(shù)據(jù)類型,在進(jìn)行賦值時會將數(shù)據(jù)直接賦值一份,將賦值的那一份數(shù)據(jù)進(jìn)行賦值,也就是通常所說的傳值;如果是數(shù)組、hash對象等復(fù)雜數(shù)據(jù)類型,在進(jìn)行賦值時會直接用內(nèi)存地址賦值,而不是將數(shù)據(jù)賦值一份,這就是傳址賦值,就是傳數(shù)據(jù)的映射地址。
[代碼8:傳值與傳址]
var a=10; // 基本數(shù)據(jù)類型 var b=a; // 將變量a保存的值賦值一份,傳給變量b,b和a各保存一份數(shù)據(jù) var c=[1,2,3]; // 復(fù)雜數(shù)據(jù)類型 var d=c; // 將變量c指向的數(shù)據(jù)內(nèi)存地址傳給變量d,c和d指向同一份數(shù)據(jù) b++; d.push(4); console.info(a); // 10 console.info(b); // 11 變量b保存的數(shù)據(jù)更改不會影響到變量a console.info(c); // 1,2,3,4 變量c和d指向同一份數(shù)據(jù),數(shù)據(jù)更改會相互影響 console.info(d); // 1,2,3,4
在原生JavaScript中,選擇傳值還是傳地址是根據(jù)數(shù)據(jù)類型來自動判斷的,但傳地址有時候會給我們帶來意想不到的麻煩,所以我們需要對復(fù)雜數(shù)據(jù)類型的賦值進(jìn)行控制,讓復(fù)雜數(shù)據(jù)類型也可以進(jìn)行傳值。
最簡單的做法是遍歷數(shù)組或者Hash對象,將數(shù)組或者Hash對象這種復(fù)雜的數(shù)據(jù)拆分成一個個簡單數(shù)據(jù),然后分別賦值,如下面代碼:
[代碼9:對復(fù)雜數(shù)據(jù)類型進(jìn)行傳值]
var a = [1, 2, 3] ,b = {name:"張三",sex:"男",tel:"1383838438"}; var c = [] ,d = {}; for(var p in a){ c[p] = a[p]; } for(var p in b){ d[p] = b[p]; } c.push("4"); d.email = "[email protected]"; console.info(a); // [1, 2, 3] console.info(c); // [1, 2, 3, "4"] console.info(b.email); // undefined console.info(d.email); // [email protected]
值得一提的是,對于數(shù)組的傳值還可以使用數(shù)組類的slice或者concat方法實(shí)現(xiàn),如下面代碼:
[代碼10:數(shù)組傳值的簡單方法]
var a = [1, 2, 3]; var b = a.slice(), c = a.concat(); b.pop(); c.push(4); console.info(a); // [1, 2, 3] console.info(b); // [1, 2] console.info(c); // [1, 2, 3, 4]
prototype本質(zhì)上也是一個hash對象,所以直接用它賦值時會進(jìn)行傳址,這也是為什么[代碼7:添加潤行為]中,zs2居然會run的原因。我們可以用for in來遍歷prototype,從而實(shí)現(xiàn)prototype的傳值。但因?yàn)閜rototype和function(用做類的function)的關(guān)系,我們還有另外一種方法實(shí)現(xiàn)prototype的傳值----new SomeFunction(),如下面代碼:
[代碼11]
// 定義Person類 function Person (name){ this.name = name; this.type = "黃"; } Person.prototype={ say : function(){ console.info("我是一個"+ this.type +"種人,我的名字叫" + this.name); } } // 定義Zhangsan類 function Zhangsan (name){ Person.call(this,name); } Zhangsan.prototype = new Person(); Zhangsan.prototype.constructor = Person; Zhangsan.prototype.run = function(){ console.info("我100米短跑只要10秒!"); } // 實(shí)例化Zhangsan對象 var zs = new Zhangsan("張三"); zs.say(); // 我是一個黃種人,我的名字叫張三 zs.run(); // 我100米短跑只要10秒! var zs2 = new Person("張三2"); zs2.run(); // TypeError: zs2.run is not a function
您是否注意到上面這句Zhangsan.prototype.constructor = Person;,這是因?yàn)閆hangsan.prototype = new Person();時,Zhangsan.prototype.constructor指向了Person,我們需要將它糾正,重新指向Zhangsan。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/79039.html
摘要:這個構(gòu)造函數(shù)的不管從調(diào)用方式還是內(nèi)部寫法就都很有的感覺,但是從用途上來說,它其實(shí)更靠近的概念是中的工廠方法。到這里,所有關(guān)于繼承的東西講完了,接下來準(zhǔn)備準(zhǔn)備說說當(dāng)中的封裝 所謂的對象,就是抽象化的數(shù)據(jù)本身 一個面向?qū)ο筠D(zhuǎn)向面向原型的困惑 我發(fā)現(xiàn)Javascript這門語言每次翻開都會帶給人新感受,尤其是看完其他語言的面向?qū)ο笤賮砜此?,但是如果你也是過來人就一定記得教科書里面冗長乏味的...
摘要:方法如示例中定義的方法靜態(tài)方法使用關(guān)鍵字修飾的方法,允許通過類名直接調(diào)用靜態(tài)方法而無需實(shí)例化。 本文同步帶你入門 JavaScript ES6 (四),轉(zhuǎn)載請注明出處。 前面我們學(xué)習(xí)了: for of 變量和擴(kuò)展語法 塊作用域變量和解構(gòu) 箭頭函數(shù) 本章我們將學(xué)習(xí) ES6 中的 類,了解類基本定義和繼承相關(guān)知識 一、概述 ES6 中的 類 是基于原型的繼承語法糖,本質(zhì)上它是一個 fu...
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
閱讀 4319·2021-09-24 09:47
閱讀 1192·2021-09-03 10:33
閱讀 2077·2019-08-30 11:13
閱讀 1039·2019-08-30 10:49
閱讀 1762·2019-08-29 16:13
閱讀 2052·2019-08-29 11:28
閱讀 3102·2019-08-26 13:31
閱讀 3638·2019-08-23 17:14