JavaScript是我在C語言之后接觸的第二門編程語言,大一暑假的時(shí)候在圖書館找了一本中國人寫的JavaScript程序設(shè)計(jì)來看。那個(gè)時(shí)候在編程方面幾乎還是小白,再加上那本書根本沒有提JavaScript的編程機(jī)制,又有一些誤導(dǎo)性的話,一直以來對(duì)JavaScript有很深的誤解,認(rèn)為JavaScript只是一門在瀏覽器上運(yùn)行的面向?qū)ο笳Z言,值此文來寫下JavaScript當(dāng)中很具有迷惑性和容易誤解的地方。當(dāng)然限于作者水平有限,也沒有什么開發(fā)經(jīng)驗(yàn),所以難免有疏漏之處,還望批評(píng)指正。
JavaScript的對(duì)象
對(duì)象是什么
JavaScript代碼當(dāng)中隨處可見new關(guān)鍵字,很容易讓人產(chǎn)生誤解,認(rèn)為JavaScript是Java一樣是基于類繼承的語言。但是事實(shí)并非如此,JavaScript當(dāng)中并沒有類,那JavaScript的對(duì)象不是類那又是什么呢?某種意義上說,JavaScript的對(duì)象就是Python當(dāng)中的字典(哈希表),其實(shí)也就是類似這樣的鍵值對(duì):
me={
"fisrtName" : "seek",
"lastName" : "truth" ,
"getName" : function(){
return this.firstName+this.lastName; //this相當(dāng)于指向這個(gè)對(duì)象的指針
}
}
這是一個(gè)比較有誤解性的地方,初次看到時(shí)候覺得有點(diǎn)無法理解,但仔細(xì)用一用還是覺得合理,我們既可以像Python一樣用[]運(yùn)算符來獲取元素,也可以用.操作符來獲取元素:
me.firstName // => seek
me["lastName"] //=> truth
me.getName() // => seektruth
new運(yùn)算符
既然JavaScript當(dāng)中是沒有類的,那么new運(yùn)算符又是在干什么呢?這是JavaScript設(shè)計(jì)的最讓人誤解的地方之一。JavaScript是一門函數(shù)式編程語言,JavaScript當(dāng)中函數(shù)是一等公民,JavaScript當(dāng)中函數(shù)也是對(duì)象,函數(shù)對(duì)象在被創(chuàng)建的時(shí)候會(huì)被添加調(diào)用屬性,比較坑的是JavaScript函數(shù)有兩種調(diào)用方式,一種是加了new關(guān)鍵字的調(diào)用,一種是沒有new關(guān)鍵字的調(diào)用,前者會(huì)返回一個(gè)對(duì)象,后者會(huì)返回return語句當(dāng)中的內(nèi)容??紤]下面的一段函數(shù):
function Obj(name){
this.name=name;
return name;
}
如果我們用new運(yùn)算符來調(diào)用:
obj = new Obj("seektruth") //obj會(huì)是一個(gè)對(duì)象:{"name": "seektruth"}
如果我們直接調(diào)用:
obj = Obj("seektruth") //obj會(huì)是一個(gè)字符串:"seektruth"
確實(shí)設(shè)計(jì)的挺坑的,我們?cè)谡{(diào)用的時(shí)候需要分清楚是否需要使用new,一般來說需要用new關(guān)鍵字來調(diào)用的函數(shù)會(huì)采用大寫開頭。
還有更坑的是如果返回的返回值是一個(gè)對(duì)象:
function Obj(name){
this.name=name;
return {};
}
這樣無論我們是否用new運(yùn)算符來調(diào)用都會(huì)返回return語句里的值:
new Obj("seektruth") //=> {}
Obj("seektruth") //=> {}
設(shè)計(jì)的是什么鬼......
對(duì)象繼承
原型
前面已經(jīng)說到過JavaScript當(dāng)中是沒有類的,那JavaScript又是怎么來實(shí)現(xiàn)繼承的呢?答案是通過原型鏈。在JavaScript當(dāng)中,每個(gè)對(duì)象都會(huì)有一個(gè)原型,在創(chuàng)建對(duì)象的時(shí)候,如果不加說明的話,對(duì)象繼承的原型是Object.prototype,函數(shù)對(duì)象會(huì)繼承Function.prototype(Function.prototype繼承Object.prototype):
Object.prototype // => {}
Function.prototype // => [Function]
我們可以通過對(duì)象的__proto__熟悉來查看對(duì)象的原型:
a={}
a.__proto__ // => {}
JavaScript通過指定對(duì)象的原型來實(shí)現(xiàn)繼承,指定對(duì)象的原型主要有三種方式,一是在構(gòu)造函數(shù)當(dāng)中指明原型,二是直接修改對(duì)象的__proto__屬性,三是利用Object.create函數(shù),下面我們依次來看一看
在構(gòu)造函數(shù)當(dāng)中指定原型
我們可以在構(gòu)造函數(shù)當(dāng)中指定對(duì)象的原型:
me={
"firstName" : "seek",
"lastName" : "truth" ,
"getName" : function(){
return this.firstName+this.lastName; //this相當(dāng)于指向這個(gè)對(duì)象的指針
}
}
function Obj(name){
this.firstName = name;
this.__proto__ = me; //指定原型為me對(duì)象
}
指定了原型之后,我們新建了對(duì)象之后就可以訪問原型的屬性:
obj = new Obj("foo"); // => { firstName: 'foo' }
obj.firstName // => foo
obj.lastName // => truth
obj.getName() // => "footruth"
當(dāng)訪問一個(gè)對(duì)象的時(shí)候,首先會(huì)嘗試在改對(duì)象當(dāng)中尋找該屬性,如果沒有就回到原型當(dāng)中尋找,直到Object.prototype。如果我們?cè)谛碌膶?duì)象當(dāng)中重寫了原型當(dāng)中的屬性(方法),那么實(shí)際使用的時(shí)候我們新寫的屬性(方法)會(huì)覆蓋掉原型當(dāng)中的定義,這有點(diǎn)像基于類的語言的函數(shù)重載。
注意如果原型me對(duì)象的lastname屬性有改變,因?yàn)閛bj對(duì)象是在原型當(dāng)中尋找屬性,那么這個(gè)obj對(duì)象的lastname屬性也會(huì)改變:
me.lastName = "me"
obj.lastName // => "me"
obj.getName() // => "foome"
直接改變對(duì)象的原型
我們也可以直接指定(改變)對(duì)象的原型:
obj2 = {}
obj2.__proto__ = me
obj2.firstName // => seek
obj2.lastName // => "me"
obj2.getName() // => "seekme"
使用Object.create函數(shù)
盡管說前兩種方法可以解決問題,但是這兩種寫法并不優(yōu)雅,因?yàn)镴avaScript并不是基于類的語言,第一寫法很容易給人以誤解,JavaScript語言精粹的作者Crockford認(rèn)為new就不應(yīng)該出現(xiàn)在JavaScript語言當(dāng)中,而推薦使用Object.create函數(shù)來基于原型來創(chuàng)建對(duì)象。Object.create函數(shù)的用法很簡單:
obj3 = Object.create(me) // 以me為原型創(chuàng)建新的對(duì)象
obj3.firstName // => seek
obj3.lastName // => "me"
obj3.getName() // => "seekme"
obj3 = Object.create(me) 與obj2 = {};obj2.proto = me是等價(jià)的,但是前一種寫法更優(yōu)雅也更易于理解。
總結(jié)
JavaScript作為一門基于原型的,函數(shù)式的編程語言在設(shè)計(jì)上有很多優(yōu)雅與強(qiáng)大之處,但同時(shí)又有很多糟粕和坑,正式如此,JavaScript也是被誤解最多語言。學(xué)習(xí)了JavaScript的對(duì)象繼承機(jī)制,感覺自己的水平還是大有長進(jìn)的。
以上這篇淺談JavaScript對(duì)象與繼承就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考
2025國考·省考課程試聽報(bào)名