淺析JavaScript原型繼承的陷阱

來源:互聯網
上載者:User

淺析JavaScript原型繼承的陷阱

 JavaScript和其它物件導向語言一樣,物件類型採用引用方式。持有對象的變數只是一個地址,而基本類型資料是值。當原型上儲存物件時,就可能有一些陷阱

JavaScript預設採用原型繼承。雖然沒有類(class)的概念,它的函數(function)可以充當構造器(constructor)。構造器結合this,new可以構建出類似Java的類。因此,JavaScript通過擴充自身能類比類式(class-based)繼承。

 

JavaScript和其它物件導向語言一樣,物件類型採用引用方式。持有對象的變數只是一個地址,而基本類型資料是值。當原型上儲存物件時,就可能有一些陷阱。

 

先看第一個例子

 

 

代碼如下:

var create = function() { 

    function Fn() {} 

    return function(parent) { 

        Fn.prototype = parent 

        return new Fn 

    } 

}() 

 

var parent = { 

    name: 'jack', 

    age: 30, 

    isMarried: false

var child = create(parent) 

console.log(child) 

 

create工具函數實現了一個基本的原型繼承,每次調用create都會根據parent對象去複製一個新對象,新對象全部的屬性都來自於parent。這裡parent有三個屬性,都是基礎資料型別 (Elementary Data Type):字串,數字,布爾。

 

這時修改child看看會不會影響parent

 

 

代碼如下:

child.name = 'lily'

child.age = 20, 

child.isMarried = true

 

console.log(child) 

console.log(parent) 

 

 

結果如下

 

 

 

即修改child不會影響到parent。

 

再看看另外一個例子

 

 

代碼如下:

var create = function() { 

    function Fn() {} 

    return function(parent) { 

        Fn.prototype = parent 

        return new Fn 

    } 

}() 

 

var parent = { 

    data: { 

        name: 'jack', 

        age: 30, 

        isMarried: false

    }, 

    language: ['Java'] 

var child = create(parent) 

 

child.data.name = 'lily'

child.data.age = 20 

child.data.isMarried = true

child.language.push('javascript') 

console.dir(child) 

console.dir(parent)

 

注意這裡的parent的兩個屬性data,language都是參考型別,一個是對象,一個是數組。child仍然繼承與parent,隨後修改了child,結果如下

 

 

 

可以看到,此時parent也被修改了,和child的name,age等都一樣了。這是使用原型繼承時需要注意的。

 

使用繼承時比較好的方式是:

 

1,資料屬性採用類式繼承(掛在this上),這樣new時也可以通過參數配置

 

2,方法採用原型繼承,這樣能節省記憶體,同時子類重寫方法也不會影響父類

 

下面是一個滿足以上2點的寫類工具函數

 

 

代碼如下:

/** 

 * @param {String} className 

 * @param {String/Function} superCls 

 * @param {Function} factory 

 */

function $class(name, superClass, factory) { 

    if (superClass === '') superClass = Object 

    function clazz() { 

        if (typeof this.init === 'function') { 

            this.init.apply(this, arguments) 

        } 

    } 

    var p = clazz.prototype = new superCls 

    clazz.prototype.constructor = clazz 

    clazz.prototype.className = className 

    var supr = superCls.prototype 

    window[className] = clazz 

    factory.call(p, supr) 

}

 

物件類型放在父類原型上時務必小心子類修改其,這時繼承於該父類的所有子類的執行個體都將被修改。而這造成的bug很不容易發現。

 

ES5中加入了一個新API用來實現原型繼承:Object.create。可以用它替代上面自實現的create函數,如下

 

 

代碼如下:

var parent = { 

    name: 'jack', 

    age: 30, 

    isMarried: false

var child = Object.create(parent) 

console.log(child) 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.