Javascript是一種動態(tài)類型語言,編寫代碼數(shù)量少,看起來簡潔勾给,但是無法保證變量的類型,那么在程序的運行期有可能發(fā)生跟類型相關(guān)的錯誤锅知。
鴨子類型
從前有個在Javascript王國里播急,有個國王,他覺得世界上最美麗的聲音就是鴨子的叫聲售睹,于是國王召集大臣桩警,要組建一個1000只鴨子組成的合唱團。大臣們找遍了全國侣姆,終于找到999只鴨子生真,但是始終還差一只,最后大臣發(fā)現(xiàn)有一只特別的雞捺宗,它的叫聲跟鴨子一模一樣柱蟀,于是這只雞就成為了合唱團的最后一員
這個故事告訴我們,國王要聽的只是鴨子的叫聲蚜厉,這個聲音的主人是鴨還是雞并不重要长已。鴨子類型指導(dǎo)我們只關(guān)注對象的行為,不關(guān)注對象本身昼牛。那Javascript就是一種這樣的語言术瓮,無需進行類型監(jiān)測,可以嘗試調(diào)用任何對象的任何方法贰健,而無需考慮原本被設(shè)計為擁有該方法胞四。
在動態(tài)類型語言的面向?qū)ο笤O(shè)計中,鴨子類型的概念至關(guān)重要伶椿。利用鴨子類型的思想辜伟,我們就能輕松地在動態(tài)類型語言中實現(xiàn)一個原則:“面向接口編程氓侧,而不是面向?qū)崿F(xiàn)編程”。
多態(tài):同一操作作用于不同對象上导狡,可以產(chǎn)生不同的解釋和不同的執(zhí)行結(jié)果约巷。
主人家里養(yǎng)了兩種動物,一只鴨旱捧,一只雞独郎。當(dāng)主人向它們發(fā)出“叫”的指令時,鴨子“嘎嘎嘎”叫枚赡,雞“咯咯咯”叫氓癌,這兩種動物會已自己的方式發(fā)出叫聲;它們同樣"都是動物贫橙,并且可以發(fā)出叫聲“顽铸,但根據(jù)主人的口令,會發(fā)出不同的叫聲料皇。
這其實隱含了多態(tài)的思想,用下面的代碼來實現(xiàn)上面的場景:
這段代碼實現(xiàn)了”多態(tài)“星压,但是如果我們要實現(xiàn)狗”汪汪汪“叫就需要修改makeSound代碼践剂,修改代碼顯然是有風(fēng)險的,當(dāng)我們種類增多時娜膘,可能makeSound就是一個巨大的函數(shù)逊脯。
多態(tài)背后的思想就是將”做什么“和”誰去做以及怎么去做“分離開來,也就是將”不變的事情“和”可能改變的事情“分離開來竣贪。把不變的部分隔離出來军洼,把變化的部分封裝起來。對于上面的例子演怎,動物叫是不變的匕争,每個動物怎么叫是可變的。
下面是修改后的代碼爷耀,把不變的makeSound分離出來甘桑,把可變的部分各自封裝起來。
如果這個世界中要再加一種會叫的動物歹叮,那只需要再封裝一個動物的對象就行了跑杭。
多態(tài)在面向?qū)ο蟪绦蛟O(shè)計中的作用
Martin Fowler 在《重構(gòu):改善既有代碼的設(shè)計》里寫到:
多態(tài)的最根本好處在于,你不必再向?qū)ο笤儐枴澳闶鞘裁搭愋汀倍蟾鶕?jù)得到的答案調(diào)用對象的某個行為——你只管調(diào)用改行為就是了咆耿,其實的一切多態(tài)機制都會為你安排妥當(dāng)
多態(tài)最根本的作用就是通過把過程化的條件分支語句轉(zhuǎn)化為對象的多態(tài)性德谅,從而消除這些條件分支語句。
將行為分布在各個對象中萨螺,并讓這些對象各自負(fù)責(zé)自己的行為窄做,這正是面向?qū)ο笤O(shè)計的優(yōu)點愧驱。
Javascript中的原型繼承
Javascript中實現(xiàn)原型繼承要遵循幾條原則
所有數(shù)據(jù)都是對象
要得到一個對象,不是通過實例化類浸策,而是找到一個對象作為原型并克隆它
對象會記住它的原型
對象如果無法響應(yīng)這個請求冯键,它會把這個請求委托給它自己的原型
Javascript在設(shè)計的時候,模仿Java引入了兩種類型:基本類型和對象類型庸汗,基本類型包括: undefined, number, boolean, string, null, object惫确。
我們不能說在Javascript中一切都是對象,但是絕大多數(shù)都是對象蚯舱,那么相信在Javascript中一定有一個根對象改化,確實Javascript中的所有對象都是從Object.prototype中克隆而來的。Object.prototype就是它們的原型枉昏。
在ECMAScript5中可以用getPrototypeo來查看對象的原型
在Javascript中沒有對象陈肛,但是剛才我們明明調(diào)用了 new 關(guān)鍵字。
在這里Person不是類兄裂,而是函數(shù)構(gòu)造器句旱,Javascript中的函數(shù)既可以作為函數(shù)構(gòu)造器使用也可以作為普通函數(shù)調(diào)用。當(dāng)使用new運算符調(diào)用函數(shù)時晰奖,此時的函數(shù)就是一個函數(shù)構(gòu)造器谈撒。用new運算符來創(chuàng)建對象的過程,實際上也只是先克隆Object.prototype對象匾南,再進行一些其他額外操作啃匿。
目前我們一直在討論“對象的原型”,就Javascript真正實現(xiàn)來說蛆楞,其實并不能說對象有原型溯乒,而只能說對象的構(gòu)造器有原型。對于“對象把請求委托給它自己的原型”這句話豹爹,更好的說法是對象把請求委托為它自己構(gòu)造器的原型裆悄。
Javascript給對象提供了一個名為proto的隱藏屬性,某個對象的proto屬性默認(rèn)會指向它的構(gòu)造器原型帅戒,即 {Constructor}.prototype灯帮。
var a = new Object()
console.log(a.proto === Object.prototype) // true
如果對象無法響應(yīng)某個請求,他會把這個請求委托給它的構(gòu)造器的原型
在運行代碼的時候逻住,引擎做了什么
首先钟哥,嘗試遍歷對象a中所有屬性,但沒有找到name 這個屬性
查找name屬性的這個請求被委托給對象a的構(gòu)造器的原型瞎访,它被a.proto記錄著并且指向A.prototype腻贰,而A.prototype被設(shè)置為對象obj。
在對象obj中找到屬性name扒秸,并返回他的值播演。
$7
$Jason 一直在路上冀瓦,我們一起上路