關(guān)于JavaScript的面向?qū)ο蠛屠^承有利新手學(xué)習(xí)
這是一篇關(guān)于JavaScript的面向?qū)ο蠛屠^承的文章,寫于1年前,作者循序漸進(jìn),對(duì)想學(xué)習(xí)JavaScript中面向?qū)ο蟮耐瑢W(xué)來說是很有幫助的,因此試著翻譯一下,不妥之處,請(qǐng)指正。原文鏈接Objects and Inheritance in Javascript
雖然一些Javascript用戶可能永遠(yuǎn)也不需要知道原型或面向?qū)ο笳Z言的性質(zhì),但是那些來自傳統(tǒng)面向?qū)ο蟮恼Z言的開發(fā)者使用的時(shí)候會(huì)發(fā)現(xiàn)JavaScript的繼承模型非常的奇怪。而不同的JS框架提供了各自的方法來編寫類面向?qū)ο?class-like)的代碼,這使得JS的面向?qū)ο蟾拥碾y以理解。這樣帶來的結(jié)果是:
1、沒有一個(gè)標(biāo)準(zhǔn)的方法來實(shí)現(xiàn)面向?qū)ο蟆?
2、JS面向?qū)ο蟮牡讓痈拍畈]有得到人們的熟知
原型繼承
原型繼承是一個(gè)非常簡單的概念,它的本質(zhì)是:
1、對(duì)象a繼承自對(duì)象b,就說b是a的原型(prototype)。
2、a繼承了b的所有的屬性,即如果b.x的值為1,那么a.x的值為1
3、a自身的屬性會(huì)重寫b中同名的屬性
讓我們?cè)诰唧w的代碼中看看效果,假設(shè)有一個(gè)John Smith和一個(gè)繼承自他的Jane。
var john = {firstName: 'John', lastName: 'Smith'};
var jane = {firstName: 'Jane'};
jane.__proto__ = john;
現(xiàn)在,我們稱john是jane的原型(prototype),jane繼承了john的所有的屬性
jane.lastName
"Smith"http://該屬性繼承自john
jane自身的屬性具有較高的優(yōu)先級(jí),如下
jane.firstName;//該屬性覆寫了john中的firstName屬性
"Jane"
如果在這之后給john添加一個(gè)屬性,jane也會(huì)動(dòng)態(tài)的繼承該屬性,如下所示
john.hair = 'brown'; //給john添加一個(gè)新屬性
jane.hair;
"brown"http://結(jié)果表明jane繼承了新添加的屬性
現(xiàn)在,我們假設(shè)jane結(jié)婚了,因此有了一個(gè)新的姓(last name)
jane.lastName = 'Doe'
該屬性覆蓋了john當(dāng)中的同名的屬性(lastName)
jane.lastName
"Doe"
但是,如果我們現(xiàn)在刪除jane的lastName
delete jane.lastName
該屬性的值就會(huì)恢復(fù)為john的值
[code]
jane.lastName
"Smith"
現(xiàn)在,jane也可以繼承自其它的對(duì)象。在這個(gè)鏈中可以有任意多個(gè)繼承,我們將它稱為原型鏈(prototype chain),實(shí)際上,john也有一個(gè)prototype屬性
john.__proto__;
Object { }
在Firebug的控制臺(tái)中,john.__proto__的值設(shè)為了Object{},但是Object{}代表著Object.prototype這個(gè)對(duì)象——所有對(duì)象的父類。
這就是對(duì)原型繼承的一個(gè)簡要描述。看起來還不錯(cuò),是吧?
但是,實(shí)際上,我們是不能用__proto__的。。。
告訴大家一個(gè)不好的消息......
IE不支持__proto__屬性,實(shí)際上,__proto__并不是ECMAScript規(guī)范中的屬性,而且,Mozilla也打算在火狐瀏覽器以后的的版本中去掉該屬性。
但是,這并不意味著__proto__屬性不存在。雖然在某些瀏覽器中無法直接訪問到__proto__屬性,但是它還是以某種形式存在,我們還得和他打交道,只不過沒有那么直接了。
類和繼承
因此,我們可以說,JavaScript沒有類
請(qǐng)記住:JavaScript中沒有類
那既然這樣,方法和繼承有事怎么實(shí)現(xiàn)的呢?
通過原型(prototype)。在傳統(tǒng)的面向?qū)ο蟮恼Z言中,方法是依賴于類的,而在JavaScript當(dāng)中,方法是依賴于對(duì)象的原型的,并且,原型是和對(duì)象的構(gòu)造器(constructor)綁定在一起的。
在JavaScript當(dāng)中,函數(shù)起到了構(gòu)造器(constructor)的作用。通過使用new運(yùn)算符,你可以把一個(gè)函數(shù)當(dāng)做構(gòu)造函數(shù)(constructor)來用。下面的代碼展示了我們創(chuàng)建了一個(gè)Cat函數(shù):
function Cat(name){ // <-這是一個(gè)常規(guī)的函數(shù)
this.name = name // this指向新建的對(duì)象
}
以上代碼會(huì)自動(dòng)創(chuàng)建一個(gè)Cat.prototype對(duì)象
Cat.prototype
Cat { }
我們可用new操作符來創(chuàng)建Cat的一個(gè)實(shí)例
var garfield = new Cat('Garfield') // 創(chuàng)建一個(gè)實(shí)例 - Cat函數(shù)充當(dāng)了構(gòu)造函數(shù)
現(xiàn)在,Cat.prototype對(duì)象成為了所有的通過new Cat()創(chuàng)建的對(duì)象的原型,例如:
garfield.__proto__ === Cat.prototype
true //看到了嗎? `Cat.prototype` 現(xiàn)在是garfield對(duì)象的原型
現(xiàn)在,我們給Cat.prototype添加一個(gè)方法,添加之后,該方法可以被garfield訪問到
Cat.prototype.greet = function(){
console.log('Meow, I am ' + this.name)
}
garfield.greet()
"Meow, I am Garfield"
其他的Cat的實(shí)例也可以訪問到
var felix = new Cat('Felix')
felix.greet()
"Meow, I am Felix"
因此,在JavaScript中,方法是依賴于對(duì)象的原型(prototype)而存在的。
實(shí)際上,我們也可以為garfield添加方法,它會(huì)覆寫Cat.prototype中的同名方法,如下所示:
garfield.greet = function(){
console.log("What's new?");
};
garfield.greet();
"What's new?"
但這并不會(huì)影響到其他的對(duì)象
felix.greet();
"Meow, I am Felix"
因此,JavaScript中,方法可以直接和一個(gè)對(duì)象關(guān)聯(lián),可以和對(duì)象的原型關(guān)聯(lián),也可以和對(duì)象的任意一個(gè)父輩對(duì)象關(guān)聯(lián),即,可以和原型鏈(prototype chain)的任意一環(huán)關(guān)聯(lián)。繼承也就是這樣實(shí)現(xiàn)的。
要?jiǎng)?chuàng)建一個(gè)二級(jí)原型鏈(prototype chain),我們首先需要?jiǎng)?chuàng)建另一個(gè)構(gòu)造函數(shù)(constructor),叫Animal如何?
function Animal(){
}
接下來,我們需要將Cat.prototype的原型指向一個(gè)Animal的對(duì)象,這樣一來,Cat的實(shí)例就會(huì)繼承所有的Animal的方法。因此,我們將Cat.prototype的值設(shè)置為Animal的實(shí)例,如下所示:
Cat.prototype = new Animal();
除此之外,我們還要告訴新的Cat.prototype,它實(shí)際上是Cat的一個(gè)實(shí)例:
Cat.prototype.constructor = Cat // 讓`Cat.prototype` 知道它是Cat的一個(gè)實(shí)例
雖然這樣做的目的主要是為了類之間的層次關(guān)系,但通常還是有必要這樣做的。
現(xiàn)在,既然繼承自Animal.prototype和Cat.prototype的對(duì)象是屬于動(dòng)物類,那么所有Cat的實(shí)例也間接的繼承自Animal.prototype。如果我們給Animal.prototype添加一個(gè)新方法,那么,所有的Cat的實(shí)例也能夠訪問到該方法。
Animal.prototype.breed = function(){
console.log('Making a new animal!');
return new this.constructor();
};
var kitty = garfield.breed();
Making a new animal!
通過上面的代碼我們就實(shí)現(xiàn)了繼承,簡單吧。
結(jié)語
雖然JavaScript中基于原型的繼承很怪并且需要花一些時(shí)間才能習(xí)慣,但是他的核心思想是很簡單的。只要你真正理解了這些本質(zhì)上的概念,你就會(huì)有信心在這些良莠不齊的代碼中駕馭JavaScript的OO。(完)^_^
- JS 面向?qū)ο笾^承---多種組合繼承詳解
- JS面向?qū)ο螅?)之Object類,靜態(tài)屬性,閉包,私有屬性, call和apply的使用,繼承的三種實(shí)現(xiàn)方法
- 學(xué)習(xí)javascript面向?qū)ο?javascript實(shí)現(xiàn)繼承的方式
- 詳解JavaScript基于面向?qū)ο笾^承實(shí)例
- 詳解JavaScript基于面向?qū)ο笾^承
- Javascript簡單實(shí)現(xiàn)面向?qū)ο缶幊汤^承實(shí)例代碼
- javascript 面向?qū)ο蠓庋b與繼承
- javaScript面向?qū)ο罄^承方法經(jīng)典實(shí)現(xiàn)
- JS Pro-深入面向?qū)ο蟮某绦蛟O(shè)計(jì)之繼承的詳解
- 徹底理解js面向?qū)ο笾^承
相關(guān)文章
javaScript合并對(duì)象的多種方式及知識(shí)擴(kuò)展
眾所周知JavaScript中有多種方法可以合并對(duì)象,下面這篇文章主要給大家介紹了關(guān)于javaScript合并對(duì)象的多種方式及知識(shí)擴(kuò)展,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02JavaScript將當(dāng)前時(shí)間轉(zhuǎn)換成UTC標(biāo)準(zhǔn)時(shí)間的方法
這篇文章主要介紹了JavaScript將當(dāng)前時(shí)間轉(zhuǎn)換成UTC標(biāo)準(zhǔn)時(shí)間的方法,涉及javascript中Date及toUTCString方法的使用技巧,需要的朋友可以參考下2015-04-04JavaScript實(shí)現(xiàn)封裝一個(gè)快速生成目錄樹的全局腳本
目錄樹可以很好的介紹項(xiàng)目中各文件目錄的用途,幫助讀者了解整個(gè)項(xiàng)目結(jié)構(gòu)。本文就來用JavaScript封裝一個(gè)快速生成目錄樹的全局腳本,希望對(duì)大家有所幫助2023-03-03javascript的23種設(shè)計(jì)模式示例總結(jié)大全
這篇文章主要為大家介紹了javascript的23種設(shè)計(jì)模式的總結(jié)大全,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06JavaScript反彈動(dòng)畫效果的實(shí)現(xiàn)代碼
本文通過實(shí)例代碼給大家介紹了js反彈動(dòng)畫效果的實(shí)現(xiàn)代碼,需要的朋友參考下吧2017-07-07style、 currentStyle、 runtimeStyle區(qū)別分析
style、 currentStyle、 runtimeStyle區(qū)別分析,需要的朋友可以參考下。2010-08-08js實(shí)現(xiàn)不重復(fù)導(dǎo)入的方法
這篇文章主要介紹了js實(shí)現(xiàn)不重復(fù)導(dǎo)入的方法,實(shí)例分析了JavaScript基于文件與字符串判斷操作實(shí)現(xiàn)JS文件不重復(fù)導(dǎo)入的相關(guān)技巧,需要的朋友可以參考下2016-03-03