亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

再談javascript原型繼承

2019-11-20 13:56:19
字體:
來源:轉載
供稿:網友

真正意義上來說Javascript并不是一門面向對象的語言,沒有提供傳統的繼承方式,但是它提供了一種原型繼承的方式,利用自身提供的原型屬性來實現繼承。

原型與原型鏈

說原型繼承之前還是要先說說原型和原型鏈,畢竟這是實現原型繼承的基礎。
在Javascript中,每個函數都有一個原型屬性prototype指向自身的原型,而由這個函數創建的對象也有一個__proto__屬性指向這個原型,而函數的原型是一個對象,所以這個對象也會有一個__proto__指向自己的原型,這樣逐層深入直到Object對象的原型,這樣就形成了原型鏈。下面這張圖很好的解釋了Javascript中的原型和原型鏈的關系。

每個函數都是Function函數創建的對象,所以每個函數也有一個__proto__屬性指向Function函數的原型。這里需要指出的是,真正形成原型鏈的是每個對象的__proto__屬性,而不是函數的prototype屬性,這是很重要的。

原型繼承

基本模式

復制代碼 代碼如下:

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

這種是最簡單實現原型繼承的方法,直接把父類的對象賦值給子類構造函數的原型,這樣子類的對象就可以訪問到父類以及父類構造函數的prototype中的屬性。 這種方法的原型繼承圖如下:

這種方法的優點很明顯,實現十分簡單,不需要任何特殊的操作;同時缺點也很明顯,如果子類需要做跟父類構造函數中相同的初始化動作,那么就得在子類構造函數中再重復一遍父類中的操作:

復制代碼 代碼如下:

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屬性,如果初始化工作不斷增加,這種方式是很不方便的。因此就有了下面一種改進的方式。

借用構造函數

復制代碼 代碼如下:

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

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

復制代碼 代碼如下:

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

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

臨時構造函數模式(圣杯模式)

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

復制代碼 代碼如下:

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

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

很容易可以看出,通過在父類原型和子類原型之間加入一個臨時的構造函數F,切斷了子類原型和父類原型之間的聯系,這樣當子類原型做修改時就不會影響到父類原型。

我的方法

《Javascript模式》中到圣杯模式就結束了,可是不管上面哪一種方法都有一個不容易被發現的問題。大家可以看到我在'Parent'的prototype屬性中加入了一個obj對象字面量屬性,但是一直都沒有用。我們在圣杯模式的基礎上來看看下面這種情況:

復制代碼 代碼如下:

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

在上面這種情況中,當我修改child對象obj.a的時候,同時父類的原型中的obj.a也會被修改,這就發生了和共享原型同樣的問題。出現這個情況是因為當訪問child.obj.a的時候,我們會沿著原型鏈一直找到父類的prototype中,然后找到了obj屬性,然后對obj.a進行修改。再看看下面這種情況:

復制代碼 代碼如下:

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

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

復制代碼 代碼如下:

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繼承的具體實現如下,這里只考慮了Child和Parent都是函數的情況下:

復制代碼 代碼如下:

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 ;
    //借用父類構造函數
    Child = function(){
        Parent.apply(this,argument) ;
    } ;
    //通過深拷貝繼承父類原型   
    Child.prototype = deepClone(Parent.prototype) ;
    //重置constructor屬性
    Child.prototype.constructor = Child ;
} ;

總結

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩一区二区三区在线免费观看| 精品视频偷偷看在线观看| 国产99视频在线观看| 日韩精品中文字幕在线观看| 国语自产精品视频在线看一大j8| 亚洲有声小说3d| 夜夜嗨av一区二区三区免费区| 高清欧美性猛交xxxx黑人猛交| 成人在线视频网| 97在线看免费观看视频在线观看| 亚洲日韩欧美视频| 国产精品一区二区电影| 国产亚洲精品久久久久久| 欧美一区在线直播| 欧美理论电影在线观看| 95av在线视频| 欧美性生交xxxxxdddd| 国产精品永久免费视频| 亚洲国产精品久久久久| 中文字幕精品—区二区| 久久精品一区中文字幕| 亚洲一级片在线看| 欧美床上激情在线观看| 国产成人精品久久二区二区91| 国产精品丝袜一区二区三区| 久久久国产成人精品| 亚洲精品视频在线观看视频| 欧美色另类天堂2015| 欧美裸体男粗大视频在线观看| 欧美人在线观看| 欧美日韩国产综合视频在线观看中文| 久久理论片午夜琪琪电影网| 日韩欧美主播在线| 国产精品极品美女在线观看免费| 欧美性视频网站| 亚洲最大中文字幕| 久久久伊人欧美| 亚洲国产成人久久| 精品自拍视频在线观看| 一区二区欧美在线| 美女国内精品自产拍在线播放| 欧美成人小视频| 国产成人综合一区二区三区| 国模精品系列视频| 视频在线观看一区二区| 欧美午夜精品久久久久久久| 亚洲国产小视频在线观看| 97国产精品视频| 亚洲精品国产suv| 国产一区二区三区在线观看网站| 最近的2019中文字幕免费一页| 97国产真实伦对白精彩视频8| 亚洲综合视频1区| 国产精品视频1区| 欧美激情第一页xxx| www亚洲精品| 国产va免费精品高清在线观看| 中文字幕日韩欧美在线视频| 日韩av电影在线网| 欧美极品少妇xxxxⅹ裸体艺术| 一本色道久久综合亚洲精品小说| 久久久久久亚洲精品中文字幕| 538国产精品一区二区免费视频| 欧美黑人巨大精品一区二区| 热久久免费视频精品| 国内精品400部情侣激情| 成人免费福利在线| 狠狠操狠狠色综合网| 日韩在线激情视频| 久久久久中文字幕2018| 色先锋久久影院av| 亚洲伊人成综合成人网| 欧美精品在线播放| 日韩精品一区二区三区第95| 日韩亚洲欧美中文在线| 国产精品欧美久久久| 97国产精品视频| 一本一本久久a久久精品综合小说| 亚洲日本欧美日韩高观看| 26uuu国产精品视频| 亚洲成色777777在线观看影院| 久久久久久九九九| 欧美国产欧美亚洲国产日韩mv天天看完整| 伊人久久大香线蕉av一区二区| 欧美亚洲免费电影| 日韩欧美亚洲一二三区| 激情成人在线视频| 中文字幕亚洲一区二区三区五十路| 日韩av片免费在线观看| 久久综合久久美利坚合众国| 欧美日韩国产精品专区| 91免费的视频在线播放| 日韩最新中文字幕电影免费看| 国产精品r级在线| 日韩成人av在线| 国产精品欧美一区二区三区奶水| 亚洲欧美在线磁力| 久久精品久久精品亚洲人| 中文字幕亚洲国产| 美女国内精品自产拍在线播放| 亚洲精品456在线播放狼人| 亚洲午夜av久久乱码| 一区二区三区回区在观看免费视频| 懂色av影视一区二区三区| www.久久久久| 亚洲香蕉av在线一区二区三区| 亚洲欧美日韩综合| 国产欧美在线观看| 国产97在线播放| 日本久久久久久| 伊人激情综合网| 欧美一区二区三区艳史| www高清在线视频日韩欧美| 欧美自拍视频在线观看| 亚洲免费伊人电影在线观看av| 欧美日韩成人在线观看| 久久久久久中文字幕| 欧美最近摘花xxxx摘花| 国产97在线播放| 91在线视频免费| 一区二区国产精品视频| 国产精品毛片a∨一区二区三区|国| 欧美美女18p| 欧美另类在线播放| 亚洲免费影视第一页| 奇米四色中文综合久久| 日韩美女激情视频| 一区二区三区四区视频| 欧美一区二区影院| 国产在线观看精品一区二区三区| 91av视频在线免费观看| 久久久久久亚洲| 91在线网站视频| 国产亚洲视频在线| 久久成人国产精品| 一区二区三区四区视频| 91精品国产99久久久久久| 国产精品久久久久久网站| 亚洲精品国产综合久久| 国产欧美日韩91| 国产精品劲爆视频| 精品国产福利在线| 欧美国产精品va在线观看| 91日本在线观看| 国产精品一区二区三区免费视频| 欧美一级视频一区二区| 亚洲男人第一网站| 在线丨暗呦小u女国产精品| 亚洲成人久久一区| 精品国产91久久久久久| 亚洲护士老师的毛茸茸最新章节| 亚洲高清久久久久久| 亚洲国产精品免费| 91免费版网站入口| 青草青草久热精品视频在线网站| 日韩中文字幕网| 亚洲欧洲日本专区| 日韩视频第一页| 欧美精品久久久久久久免费观看| 欧美电影免费播放| 亚洲一级片在线看| 91精品久久久久久久久久另类| 久久精品亚洲国产| 亚洲欧美国产另类|