淺析JavaScript原型繼承的陷阱
JavaScript默認(rèn)采用原型繼承。雖然沒(méi)有類(lèi)(class)的概念,它的函數(shù)(function)可以充當(dāng)構(gòu)造器(constructor)。構(gòu)造器結(jié)合this,new可以構(gòu)建出類(lèi)似Java的類(lèi)。因此,JavaScript通過(guò)擴(kuò)展自身能模擬類(lèi)式(class-based)繼承。
JavaScript和其它面向?qū)ο笳Z(yǔ)言一樣,對(duì)象類(lèi)型采用引用方式。持有對(duì)象的變量只是一個(gè)地址,而基本類(lèi)型數(shù)據(jù)是值。當(dāng)原型上存儲(chǔ)對(duì)象時(shí),就可能有一些陷阱。
先看第一個(gè)例子
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工具函數(shù)實(shí)現(xiàn)了一個(gè)基本的原型繼承,每次調(diào)用create都會(huì)根據(jù)parent對(duì)象去復(fù)制一個(gè)新對(duì)象,新對(duì)象全部的屬性都來(lái)自于parent。這里parent有三個(gè)屬性,都是基本數(shù)據(jù)類(lèi)型:字符串,數(shù)字,布爾。
這時(shí)修改child看看會(huì)不會(huì)影響parent
child.name = 'lily'
child.age = 20,
child.isMarried = true
console.log(child)
console.log(parent)
結(jié)果如下
即修改child不會(huì)影響到parent。
再看看另外一個(gè)例子
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的兩個(gè)屬性data,language都是引用類(lèi)型,一個(gè)是對(duì)象,一個(gè)是數(shù)組。child仍然繼承與parent,隨后修改了child,結(jié)果如下
可以看到,此時(shí)parent也被修改了,和child的name,age等都一樣了。這是使用原型繼承時(shí)需要注意的。
使用繼承時(shí)比較好的方式是:
1,數(shù)據(jù)屬性采用類(lèi)式繼承(掛在this上),這樣new時(shí)也可以通過(guò)參數(shù)配置
2,方法采用原型繼承,這樣能節(jié)省內(nèi)存,同時(shí)子類(lèi)重寫(xiě)方法也不會(huì)影響父類(lèi)
下面是一個(gè)滿(mǎn)足以上2點(diǎn)的寫(xiě)類(lèi)工具函數(shù)
/**
* @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)
}
對(duì)象類(lèi)型放在父類(lèi)原型上時(shí)務(wù)必小心子類(lèi)修改其,這時(shí)繼承于該父類(lèi)的所有子類(lèi)的實(shí)例都將被修改。而這造成的bug很不容易發(fā)現(xiàn)。
ES5中加入了一個(gè)新API用來(lái)實(shí)現(xiàn)原型繼承:Object.create??梢杂盟娲厦孀詫?shí)現(xiàn)的create函數(shù),如下
var parent = {
name: 'jack',
age: 30,
isMarried: false
}
var child = Object.create(parent)
console.log(child)
相關(guān)文章
jQuery Mobile動(dòng)態(tài)刷新頁(yè)面樣式的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇jQuery Mobile動(dòng)態(tài)刷新頁(yè)面樣式的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05JS、CSS和HTML實(shí)現(xiàn)注冊(cè)頁(yè)面
這篇文章主要為大家詳細(xì)介紹了JS、CSS和HTML實(shí)現(xiàn)注冊(cè)頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07使用SpreadJS快速清除Excel中工作表保護(hù)密碼
這篇文章主要為大家介紹了使用SpreadJS快速清除Excel中工作表保護(hù)密碼方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11ThingJS粒子特效一鍵實(shí)現(xiàn)雨雪效果
在做3D項(xiàng)目時(shí),我們經(jīng)常需要模擬下雨,下雪的天氣,有時(shí)也會(huì)模擬噴泉、著火等效果。這些效果需要使用名為粒子系統(tǒng)(particle)的技術(shù)來(lái)實(shí)現(xiàn)。使用ThingJS可以快速編寫(xiě)粒子效果,本文就來(lái)看看如何實(shí)現(xiàn)2021-05-05鼠標(biāo)懸浮停留三秒后自動(dòng)顯示大圖js代碼
這篇文章主要介紹了鼠標(biāo)懸浮停留三秒后顯示大圖,在網(wǎng)頁(yè)中還是比較實(shí)用的,下面是示例代碼2014-09-09