[js高手之路]從原型鏈開(kāi)始圖解繼承到組合繼承的產(chǎn)生詳解
于javascript原型鏈的層層遞進(jìn)查找規(guī)則,以及原型對(duì)象(prototype)的共享特性,實(shí)現(xiàn)繼承是非常簡(jiǎn)單的事情
一、把父類的實(shí)例對(duì)象賦給子類的原型對(duì)象(prototype),可以實(shí)現(xiàn)繼承
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher (){} Teacher.prototype = new Person(); var oT = new Teacher(); console.log( oT.userName ); //ghostwu console.log( oT.showUserName() ); //ghostwu
通過(guò)把父類(Person)的一個(gè)實(shí)例賦給子類Teacher的原型對(duì)象,就可以實(shí)現(xiàn)繼承,子類的實(shí)例就可以訪問(wèn)到父類的屬性和方法
如果你不會(huì)畫這個(gè)圖,你需要去看下我的這篇文章:
[js高手之路]一步步圖解javascript的原型(prototype)對(duì)象,原型鏈
第11行,執(zhí)行oT.userName, 首先去oT對(duì)象上查找,很明顯oT對(duì)象上沒(méi)有任何屬性,所以就順著oT的隱式原型__proto__的指向查找到Teacher.prototype,
發(fā)現(xiàn)還是沒(méi)有userName這個(gè)屬性,繼續(xù)沿著Teacher.prototype.__proto__向上查找,找到了new Person() 這個(gè)實(shí)例上面有個(gè)userName,值為ghostwu
所以停止查找,輸出ghostwu.
第12行,執(zhí)行oT.showUserName前面的過(guò)程同上,但是在new Person()這個(gè)實(shí)例上還是沒(méi)有查找到showUserName這個(gè)方法,繼續(xù)沿著new Person()的
隱式原型__proto__的指向( Person.prototype )查找,在Person.prototype上找到了showUserName這個(gè)方法,停止查找,輸出ghostwu.
二、把父類的原型對(duì)象(prototype)賦給子類的原型對(duì)象(prototype),可以繼承到父類的方法,但是繼承不到父類的屬性
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return 'Person::showUserName方法'; } function Teacher (){} Teacher.prototype = Person.prototype; var oT = new Teacher(); console.log( oT.showUserName() ); //ghostwu console.log( oT.userName ); //undefined, 沒(méi)有繼承到父類的userName
因?yàn)門eacher.prototype的隱式原型(__proto__)只指向Person.prototype,所以獲取不到Person實(shí)例的屬性
三、發(fā)生繼承關(guān)系后,實(shí)例與構(gòu)造函數(shù)(類)的關(guān)系判斷
還是通過(guò)instanceof和isPrototypeOf判斷
function Person(){ this.userName = 'ghostwu'; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher (){} Teacher.prototype = new Person(); var oT = new Teacher(); console.log( oT instanceof Teacher ); //true console.log( oT instanceof Person ); //true console.log( oT instanceof Object ); //true console.log( Teacher.prototype.isPrototypeOf( oT ) ); //true console.log( Person.prototype.isPrototypeOf( oT ) ); //true console.log( Object.prototype.isPrototypeOf( oT ) ); //true
四,父類存在的方法和屬性,子類可以覆蓋(重寫),子類沒(méi)有的方法和屬性,可以擴(kuò)展
function Person() {} Person.prototype.showUserName = function () { console.log('Person::showUserName'); } function Teacher() { } Teacher.prototype = new Person(); Teacher.prototype.showUserName = function(){ console.log('Teacher::showUserName'); } Teacher.prototype.showAge = function(){ console.log( 22 ); } var oT = new Teacher(); oT.showUserName(); //Teacher::showUserName oT.showAge(); //22
五、重寫原型對(duì)象之后,其實(shí)就是把原型對(duì)象的__proto__的指向發(fā)生了改變
原型對(duì)象prototype的__proto__的指向發(fā)生了改變,會(huì)把原本的繼承關(guān)系覆蓋(切斷)
function Person() {} Person.prototype.showUserName = function () { console.log('Person::showUserName'); } function Teacher() {} Teacher.prototype = new Person(); Teacher.prototype = { showAge : function(){ console.log( 22 ); } } var oT = new Teacher(); oT.showAge(); //22 oT.showUserName();
上例,第7行,Teacher.prototype重寫了Teacher的原型對(duì)象(prototype),原來(lái)第6行的原型對(duì)象的隱式原型(__proto__)指向就沒(méi)有作用了
所以在第14行,oT.showUserName() 就會(huì)發(fā)生調(diào)用錯(cuò)誤,因?yàn)門eacher的原型對(duì)象(prototype)的隱式原型(__proto__)不再指向父類(Person)的實(shí)例,繼承關(guān)系被破壞了.
六、在繼承過(guò)程中,小心處理實(shí)例的屬性上引用類型的數(shù)據(jù)
function Person(){ this.skills = [ 'php', 'javascript' ]; } function Teacher (){} Teacher.prototype = new Person(); var oT1 = new Teacher(); var oT2 = new Teacher(); oT1.skills.push( 'linux' ); console.log( oT2.skills ); //php, java, linux
oT1的skills添加了一項(xiàng)linux數(shù)據(jù),其他的實(shí)例都能訪問(wèn)到,因?yàn)槠渌麑?shí)例中共享了skills數(shù)據(jù),skills是一個(gè)引用類型
七、借用構(gòu)造函數(shù)
為了消除引用類型影響不同的實(shí)例,可以借用構(gòu)造函數(shù),把引用類型的數(shù)據(jù)復(fù)制到每個(gè)對(duì)象上,就不會(huì)相互影響了
function Person( uName ){ this.skills = [ 'php', 'javascript' ]; this.userName = uName; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher ( uName ){ Person.call( this, uName ); } var oT1 = new Teacher(); oT1.skills.push( 'linux' ); var oT2 = new Teacher(); console.log( oT2.skills ); //php,javascript console.log( oT2.showUserName() );
雖然oT1.skills添加了一項(xiàng)Linux,但是不會(huì)影響oT2.skills的數(shù)據(jù),通過(guò)子類構(gòu)造函數(shù)中call的方式,去借用父類的構(gòu)造函數(shù),把父類的屬性復(fù)制過(guò)來(lái),而且還能
傳遞參數(shù),如第8行,但是第15行,方法調(diào)用錯(cuò)誤,因?yàn)樵跇?gòu)造中只復(fù)制了屬性,不會(huì)復(fù)制到父類原型對(duì)象上的方法
八、組合繼承(原型對(duì)象+借用構(gòu)造函數(shù))
經(jīng)過(guò)以上的分析, 單一的原型繼承的缺點(diǎn)有:
1、不能傳遞參數(shù),如,
Teacher.prototype = new Person();
有些人說(shuō),小括號(hào)后面可以跟參數(shù)啊,沒(méi)錯(cuò),但是只要跟了參數(shù),子類所有的實(shí)例屬性,都是跟這個(gè)一樣,說(shuō)白了,還是傳遞不了參數(shù)
2、把引用類型放在原型對(duì)象上,會(huì)在不同實(shí)例上產(chǎn)生相互影響
單一的借用構(gòu)造函數(shù)的缺點(diǎn):
1、不能復(fù)制到父類的方法
剛好原型對(duì)象方式的缺點(diǎn),借用構(gòu)造函數(shù)可以彌補(bǔ),借用構(gòu)造函數(shù)的缺點(diǎn),原型對(duì)象方式可以彌補(bǔ),于是,就產(chǎn)生了一種組合繼承方法:
function Person( uName ){ this.skills = [ 'php', 'javascript' ]; this.userName = uName; } Person.prototype.showUserName = function(){ return this.userName; } function Teacher ( uName ){ Person.call( this, uName ); } Teacher.prototype = new Person(); var oT1 = new Teacher( 'ghostwu' ); oT1.skills.push( 'linux' ); var oT2 = new Teacher( 'ghostwu' ); console.log( oT2.skills ); //php,javascript console.log( oT2.showUserName() ); //ghostwu
子類實(shí)例oT2的skills不會(huì)受到oT1的影響,子類的實(shí)例也能調(diào)用到父類的方法。
以上這篇[js高手之路]從原型鏈開(kāi)始圖解繼承到組合繼承的產(chǎn)生詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript Html實(shí)現(xiàn)移動(dòng)端紅包雨功能頁(yè)面
這篇文章主要為大家詳細(xì)介紹了JavaScript Html實(shí)現(xiàn)移動(dòng)端紅包雨功能頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01JavaScript實(shí)現(xiàn)元素滾動(dòng)條到達(dá)一定位置循環(huán)追加內(nèi)容
下面小編就為大家分享一篇JavaScript實(shí)現(xiàn)元素滾動(dòng)條到達(dá)一定位置循環(huán)追加內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-12-12js 點(diǎn)擊按鈕彈出另一頁(yè),選擇值后,返回到當(dāng)前頁(yè)
js 點(diǎn)擊按鈕彈出另一頁(yè),選擇值后,返回到當(dāng)前頁(yè),其實(shí)主要用于cms系統(tǒng)中,相關(guān)文章的搜索,要在已上傳目錄中選擇一些圖片等。2010-05-05JS 組件系列之 bootstrap treegrid 組件封裝過(guò)程
最近產(chǎn)品需要設(shè)計(jì)一套相對(duì)完整的組織架構(gòu)的解決方案,由于組織架構(gòu)涉及到層級(jí)關(guān)系,在表格里面展示層級(jí)關(guān)系,自然就要用到所謂的treegrid。下面小編通過(guò)本文給大家分享JS 組件系列之 bootstrap treegrid 組件的封裝過(guò)程,需要的朋友可以參考下2017-04-04javascript 關(guān)于賦值、淺拷貝、深拷貝的個(gè)人理解
關(guān)于賦值、淺拷貝、深拷貝,以前也思考良久,很多時(shí)候都以為記住了,但是,我太難了。今天我特地寫下筆記,希望可以完全掌握這個(gè)東西,也希望可以幫助到任何想對(duì)學(xué)習(xí)這個(gè)東西的同學(xué)2019-11-11實(shí)現(xiàn)div內(nèi)部滾動(dòng)條滾動(dòng)到底部和頂部的代碼
下面筆者就為大家分享一篇實(shí)現(xiàn)div內(nèi)部滾動(dòng)條滾動(dòng)到底部和頂部的代碼,代碼簡(jiǎn)潔,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11