淺談JavaScript對象與繼承
JavaScript是我在C語言之后接觸的第二門編程語言,大一暑假的時(shí)候在圖書館找了一本中國人寫的JavaScript程序設(shè)計(jì)來看。那個(gè)時(shí)候在編程方面幾乎還是小白,再加上那本書根本沒有提JavaScript的編程機(jī)制,又有一些誤導(dǎo)性的話,一直以來對JavaScript有很深的誤解,認(rèn)為JavaScript只是一門在瀏覽器上運(yùn)行的面向?qū)ο笳Z言,值此文來寫下JavaScript當(dāng)中很具有迷惑性和容易誤解的地方。當(dāng)然限于作者水平有限,也沒有什么開發(fā)經(jīng)驗(yàn),所以難免有疏漏之處,還望批評指正。
JavaScript的對象
對象是什么
JavaScript代碼當(dāng)中隨處可見new關(guān)鍵字,很容易讓人產(chǎn)生誤解,認(rèn)為JavaScript是Java一樣是基于類繼承的語言。但是事實(shí)并非如此,JavaScript當(dāng)中并沒有類,那JavaScript的對象不是類那又是什么呢?某種意義上說,JavaScript的對象就是Python當(dāng)中的字典(哈希表),其實(shí)也就是類似這樣的鍵值對:
me={ "fisrtName" : "seek", "lastName" : "truth" , "getName" : function(){ return this.firstName+this.lastName; //this相當(dāng)于指向這個(gè)對象的指針 } }
這是一個(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ù)也是對象,函數(shù)對象在被創(chuàng)建的時(shí)候會(huì)被添加調(diào)用屬性,比較坑的是JavaScript函數(shù)有兩種調(diào)用方式,一種是加了new關(guān)鍵字的調(diào)用,一種是沒有new關(guān)鍵字的調(diào)用,前者會(huì)返回一個(gè)對象,后者會(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è)對象:{"name": "seektruth"}
如果我們直接調(diào)用:
obj = Obj("seektruth") //obj會(huì)是一個(gè)字符串:"seektruth"
確實(shí)設(shè)計(jì)的挺坑的,我們在調(diào)用的時(shí)候需要分清楚是否需要使用new,一般來說需要用new關(guān)鍵字來調(diào)用的函數(shù)會(huì)采用大寫開頭。
還有更坑的是如果返回的返回值是一個(gè)對象:
function Obj(name){ this.name=name; return {}; }
這樣無論我們是否用new運(yùn)算符來調(diào)用都會(huì)返回return語句里的值:
new Obj("seektruth") //=> {} Obj("seektruth") //=> {}
設(shè)計(jì)的是什么鬼......
對象繼承
原型
前面已經(jīng)說到過JavaScript當(dāng)中是沒有類的,那JavaScript又是怎么來實(shí)現(xiàn)繼承的呢?答案是通過原型鏈。在JavaScript當(dāng)中,每個(gè)對象都會(huì)有一個(gè)原型,在創(chuàng)建對象的時(shí)候,如果不加說明的話,對象繼承的原型是Object.prototype,函數(shù)對象會(huì)繼承Function.prototype(Function.prototype繼承Object.prototype):
Object.prototype // => {} Function.prototype // => [Function]
我們可以通過對象的__proto__熟悉來查看對象的原型:
a={} a.__proto__ // => {}
JavaScript通過指定對象的原型來實(shí)現(xiàn)繼承,指定對象的原型主要有三種方式,一是在構(gòu)造函數(shù)當(dāng)中指明原型,二是直接修改對象的__proto__屬性,三是利用Object.create函數(shù),下面我們依次來看一看
在構(gòu)造函數(shù)當(dāng)中指定原型
我們可以在構(gòu)造函數(shù)當(dāng)中指定對象的原型:
me={ "firstName" : "seek", "lastName" : "truth" , "getName" : function(){ return this.firstName+this.lastName; //this相當(dāng)于指向這個(gè)對象的指針 } } function Obj(name){ this.firstName = name; this.__proto__ = me; //指定原型為me對象 }
指定了原型之后,我們新建了對象之后就可以訪問原型的屬性:
obj = new Obj("foo"); // => { firstName: 'foo' } obj.firstName // => foo obj.lastName // => truth obj.getName() // => "footruth"
當(dāng)訪問一個(gè)對象的時(shí)候,首先會(huì)嘗試在改對象當(dāng)中尋找該屬性,如果沒有就回到原型當(dāng)中尋找,直到Object.prototype。如果我們在新的對象當(dāng)中重寫了原型當(dāng)中的屬性(方法),那么實(shí)際使用的時(shí)候我們新寫的屬性(方法)會(huì)覆蓋掉原型當(dāng)中的定義,這有點(diǎn)像基于類的語言的函數(shù)重載。
注意如果原型me對象的lastname屬性有改變,因?yàn)閛bj對象是在原型當(dāng)中尋找屬性,那么這個(gè)obj對象的lastname屬性也會(huì)改變:
me.lastName = "me" obj.lastName // => "me" obj.getName() // => "foome"
直接改變對象的原型
我們也可以直接指定(改變)對象的原型:
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)建對象。Object.create函數(shù)的用法很簡單:
obj3 = Object.create(me) // 以me為原型創(chuàng)建新的對象 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的對象繼承機(jī)制,感覺自己的水平還是大有長進(jìn)的。
以上這篇淺談JavaScript對象與繼承就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript實(shí)現(xiàn)職責(zé)鏈模式概述
這篇文章主要介紹了JavaScript實(shí)現(xiàn)職責(zé)鏈模式概述,詳細(xì)的介紹了什么是職責(zé)鏈模式和實(shí)現(xiàn)方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01JS中使用textPath實(shí)現(xiàn)線條上的文字
最近項(xiàng)目經(jīng)理交給我一下新項(xiàng)目,要實(shí)現(xiàn)關(guān)系圖,需要在線條上繪制文字。下面小編把使用textPath實(shí)現(xiàn)線條上的文字功能分享到腳本之家平臺供大家參考2017-12-12js canvas實(shí)現(xiàn)驗(yàn)證碼并獲取驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了js canvas實(shí)現(xiàn)驗(yàn)證碼并獲取驗(yàn)證碼功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09比較簡單實(shí)用的使用正則三種版本的js去空格處理方法
比較簡單實(shí)用的使用正則三種版本的js去空格處理方法...2007-11-11如何讓一個(gè)json文件顯示在表格里【實(shí)現(xiàn)代碼】
如何讓一個(gè)json文件顯示在表格里?下面小編就為大家?guī)硪黄屢粋€(gè)json文件顯示在表格里的實(shí)現(xiàn)代碼。希望對大家有所幫助。一起跟隨小編過來看看吧,祝大家游戲愉快哦2016-05-05微信小程序全局變量改變監(jiān)聽的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于微信小程序全局變量改變監(jiān)聽的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用微信小程序具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07JavaScript實(shí)現(xiàn)點(diǎn)擊切換驗(yàn)證碼及校驗(yàn)
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)點(diǎn)擊切換驗(yàn)證碼及校驗(yàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01EXT中單擊button按鈕grid添加一行(光標(biāo)位置可設(shè)置)的實(shí)例代碼
這篇文章主要介紹了EXT中單擊button按鈕grid添加一行(光標(biāo)位置可設(shè)置)的實(shí)例代碼 的相關(guān)資料,需要的朋友可以參考下2016-06-06