欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

再談javascript原型繼承

 更新時(shí)間:2014年11月10日 11:44:50   投稿:hebedich  
Javascript原型繼承是一個(gè)被說爛掉了的話題,但是自己對(duì)于這個(gè)問題一直沒有徹底理解,今天花了點(diǎn)時(shí)間又看了一遍《Javascript模式》中關(guān)于原型實(shí)現(xiàn)繼承的幾種方法,下面來一一說明下,在最后我根據(jù)自己的理解提出了一個(gè)關(guān)于繼承比較完整的實(shí)現(xiàn)。

真正意義上來說Javascript并不是一門面向?qū)ο蟮恼Z言,沒有提供傳統(tǒng)的繼承方式,但是它提供了一種原型繼承的方式,利用自身提供的原型屬性來實(shí)現(xiàn)繼承。

原型與原型鏈

說原型繼承之前還是要先說說原型和原型鏈,畢竟這是實(shí)現(xiàn)原型繼承的基礎(chǔ)。
在Javascript中,每個(gè)函數(shù)都有一個(gè)原型屬性prototype指向自身的原型,而由這個(gè)函數(shù)創(chuàng)建的對(duì)象也有一個(gè)__proto__屬性指向這個(gè)原型,而函數(shù)的原型是一個(gè)對(duì)象,所以這個(gè)對(duì)象也會(huì)有一個(gè)__proto__指向自己的原型,這樣逐層深入直到Object對(duì)象的原型,這樣就形成了原型鏈。下面這張圖很好的解釋了Javascript中的原型和原型鏈的關(guān)系。

每個(gè)函數(shù)都是Function函數(shù)創(chuàng)建的對(duì)象,所以每個(gè)函數(shù)也有一個(gè)__proto__屬性指向Function函數(shù)的原型。這里需要指出的是,真正形成原型鏈的是每個(gè)對(duì)象的__proto__屬性,而不是函數(shù)的prototype屬性,這是很重要的。

原型繼承

基本模式

復(fù)制代碼 代碼如下:

var Parent = function(){
    this.name = 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(){
    this.name = 'child' ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent() ;
var child = new Child() ;

console.log(parent.getName()) ; //parent
console.log(child.getName()) ; //child

這種是最簡單實(shí)現(xiàn)原型繼承的方法,直接把父類的對(duì)象賦值給子類構(gòu)造函數(shù)的原型,這樣子類的對(duì)象就可以訪問到父類以及父類構(gòu)造函數(shù)的prototype中的屬性。 這種方法的原型繼承圖如下:

這種方法的優(yōu)點(diǎn)很明顯,實(shí)現(xiàn)十分簡單,不需要任何特殊的操作;同時(shí)缺點(diǎn)也很明顯,如果子類需要做跟父類構(gòu)造函數(shù)中相同的初始化動(dòng)作,那么就得在子類構(gòu)造函數(shù)中再重復(fù)一遍父類中的操作:

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    this.name = name || 'child' ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

上面這種情況還只是需要初始化name屬性,如果初始化工作不斷增加,這種方式是很不方便的。因此就有了下面一種改進(jìn)的方式。

借用構(gòu)造函數(shù)

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

上面這種方法在子類構(gòu)造函數(shù)中通過apply調(diào)用父類的構(gòu)造函數(shù)來進(jìn)行相同的初始化工作,這樣不管父類中做了多少初始化工作,子類也可以執(zhí)行同樣的初始化工作。但是上面這種實(shí)現(xiàn)還存在一個(gè)問題,父類構(gòu)造函數(shù)被執(zhí)行了兩次,一次是在子類構(gòu)造函數(shù)中,一次在賦值子類原型時(shí),這是很多余的,所以我們還需要做一個(gè)改進(jìn):

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = Parent.prototype ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

這樣我們就只需要在子類構(gòu)造函數(shù)中執(zhí)行一次父類的構(gòu)造函數(shù),同時(shí)又可以繼承父類原型中的屬性,這也比較符合原型的初衷,就是把需要復(fù)用的內(nèi)容放在原型中,我們也只是繼承了原型中可復(fù)用的內(nèi)容。上面這種方式的原型圖如下:

臨時(shí)構(gòu)造函數(shù)模式(圣杯模式)

上面借用構(gòu)造函數(shù)模式最后改進(jìn)的版本還是存在問題,它把父類的原型直接賦值給子類的原型,這就會(huì)造成一個(gè)問題,就是如果對(duì)子類的原型做了修改,那么這個(gè)修改同時(shí)也會(huì)影響到父類的原型,進(jìn)而影響父類對(duì)象,這個(gè)肯定不是大家所希望看到的。為了解決這個(gè)問題就有了臨時(shí)構(gòu)造函數(shù)模式。

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

該方法的原型繼承圖如下:

很容易可以看出,通過在父類原型和子類原型之間加入一個(gè)臨時(shí)的構(gòu)造函數(shù)F,切斷了子類原型和父類原型之間的聯(lián)系,這樣當(dāng)子類原型做修改時(shí)就不會(huì)影響到父類原型。

我的方法

《Javascript模式》中到圣杯模式就結(jié)束了,可是不管上面哪一種方法都有一個(gè)不容易被發(fā)現(xiàn)的問題。大家可以看到我在'Parent'的prototype屬性中加入了一個(gè)obj對(duì)象字面量屬性,但是一直都沒有用。我們?cè)谑ケJ降幕A(chǔ)上來看看下面這種情況:

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

在上面這種情況中,當(dāng)我修改child對(duì)象obj.a的時(shí)候,同時(shí)父類的原型中的obj.a也會(huì)被修改,這就發(fā)生了和共享原型同樣的問題。出現(xiàn)這個(gè)情況是因?yàn)楫?dāng)訪問child.obj.a的時(shí)候,我們會(huì)沿著原型鏈一直找到父類的prototype中,然后找到了obj屬性,然后對(duì)obj.a進(jìn)行修改。再看看下面這種情況:

復(fù)制代碼 代碼如下:

var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

這里有一個(gè)關(guān)鍵的問題,當(dāng)對(duì)象訪問原型中的屬性時(shí),原型中的屬性對(duì)于對(duì)象來說是只讀的,也就是說child對(duì)象可以讀取obj對(duì)象,但是無法修改原型中obj對(duì)象引用,所以當(dāng)child修改obj的時(shí)候并不會(huì)對(duì)原型中的obj產(chǎn)生影響,它只是在自身對(duì)象添加了一個(gè)obj屬性,覆蓋了父類原型中的obj屬性。而當(dāng)child對(duì)象修改obj.a時(shí),它先讀取了原型中obj的引用,這時(shí)候child.obj和Parent.prototype.obj是指向同一個(gè)對(duì)象的,所以child對(duì)obj.a的修改會(huì)影響到Parent.prototype.obj.a的值,進(jìn)而影響父類的對(duì)象。AngularJS中關(guān)于$scope嵌套的繼承方式就是模范Javasript中的原型繼承來實(shí)現(xiàn)的。
根據(jù)上面的描述,只要子類對(duì)象中訪問到的原型跟父類原型是同一個(gè)對(duì)象,那么就會(huì)出現(xiàn)上面這種情況,所以我們可以對(duì)父類原型進(jìn)行拷貝然后再賦值給子類原型,這樣當(dāng)子類修改原型中的屬性時(shí)就只是修改父類原型的一個(gè)拷貝,并不會(huì)影響到父類原型。具體實(shí)現(xiàn)如下:

復(fù)制代碼 代碼如下:

var deepClone = function(source,target){
    source = source || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = '[object array]' ;
    for(var i in source){
        if(source.hasOwnProperty(i)){
            var item = source[i] ;
            if(typeof item === 'object'){
                target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }else{
                deepClone(item,target[i]) ;
            }
        }
    }
    return target ;
} ;
var Parent = function(name){
    this.name = name || 'parent' ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : '1'} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;

var child = new Child('child') ;
var parent = new Parent('parent') ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = '2' ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //1


綜合上面所有的考慮,Javascript繼承的具體實(shí)現(xiàn)如下,這里只考慮了Child和Parent都是函數(shù)的情況下:

復(fù)制代碼 代碼如下:

var deepClone = function(source,target){
    source = source || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = '[object array]' ;
    for(var i in source){
        if(source.hasOwnProperty(i)){
            var item = source[i] ;
            if(typeof item === 'object'){
                target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }else{
                deepClone(item,target[i]) ;
            }
        }
    }
    return target ;
} ;

var extend = function(Parent,Child){
    Child = Child || function(){} ;
    if(Parent === undefined)
        return Child ;
    //借用父類構(gòu)造函數(shù)
    Child = function(){
        Parent.apply(this,argument) ;
    } ;
    //通過深拷貝繼承父類原型   
    Child.prototype = deepClone(Parent.prototype) ;
    //重置constructor屬性
    Child.prototype.constructor = Child ;
} ;

總結(jié)

說了這么多,其實(shí)Javascript中實(shí)現(xiàn)繼承是十分靈活多樣的,并沒有一種最好的方法,需要根據(jù)不同的需求實(shí)現(xiàn)不同方式的繼承,最重要的是要理解Javascript中實(shí)現(xiàn)繼承的原理,也就是原型和原型鏈的問題,只要理解了這些,自己實(shí)現(xiàn)繼承就可以游刃有余。

相關(guān)文章

  • JavaScript獲取XML數(shù)據(jù)附示例截圖

    JavaScript獲取XML數(shù)據(jù)附示例截圖

    這篇文章主要介紹了JavaScript獲取XML數(shù)據(jù)的方法,需要的朋友可以參考下
    2014-03-03
  • js 計(jì)算月/周的第一天和最后一天代碼

    js 計(jì)算月/周的第一天和最后一天代碼

    這篇文章主要介紹了js 計(jì)算月/周的第一天和最后一天代碼,需要的朋友可以參考下
    2020-02-02
  • window.open()實(shí)現(xiàn)post傳遞參數(shù)

    window.open()實(shí)現(xiàn)post傳遞參數(shù)

    本文主要向大家介紹了如何使用window.open()實(shí)現(xiàn)post傳遞參數(shù)的方法,思路是參考的一位網(wǎng)友的,然后根據(jù)自己的項(xiàng)目需求做了些調(diào)整,這里同樣分享給大家,希望對(duì)大家能夠有所幫助。
    2015-03-03
  • 微信小程序自定義菜單切換欄tabbar組件代碼實(shí)例

    微信小程序自定義菜單切換欄tabbar組件代碼實(shí)例

    這篇文章主要介紹了微信小程序自定義菜單切換欄tabbar組件代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 詳解babel是如何將class語法糖轉(zhuǎn)換為es5的語法

    詳解babel是如何將class語法糖轉(zhuǎn)換為es5的語法

    這篇文章主要詳細(xì)介紹了babel是如何將class語法糖轉(zhuǎn)換為es5的語法,文中通過代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-02-02
  • 檢測(cè)用戶按鍵

    檢測(cè)用戶按鍵

    檢測(cè)用戶按鍵...
    2006-07-07
  • 如何消除inline-block屬性帶來的標(biāo)簽間間隙

    如何消除inline-block屬性帶來的標(biāo)簽間間隙

    這篇文章主要介紹了如何消除inline-block屬性帶來的標(biāo)簽間間隙的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • D3.js實(shí)現(xiàn)力向?qū)D的繪制教程詳解

    D3.js實(shí)現(xiàn)力向?qū)D的繪制教程詳解

    力向?qū)D是繪圖的一種算法,實(shí)現(xiàn)了用以模擬粒子物理運(yùn)動(dòng)的?velocity?Verlet?數(shù)值積分器。本文將利用D3.js實(shí)現(xiàn)力向?qū)D的繪制,需要的可以參考一下
    2022-11-11
  • 淺談js中的閉包

    淺談js中的閉包

    閉包是一個(gè)比較抽象的概念,尤其是對(duì)js新手來說.書上的解釋實(shí)在是比較晦澀,對(duì)我來說也是一樣.閉包是很多語言都具備的特性,在js中,閉包主要涉及到j(luò)s的幾個(gè)其他的特性:作用域鏈,垃圾(內(nèi)存)回收機(jī)制,函數(shù)嵌套,等等.
    2015-03-03
  • JavaScript 截取字符串代碼實(shí)例

    JavaScript 截取字符串代碼實(shí)例

    這篇文章主要介紹了JavaScript 截取字符串代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09

最新評(píng)論