面向?qū)ο笫莏s最難弄懂的一部分,我在看了《Javascript高級程序設(shè)計》之后還看了李炎恢老師關(guān)于面向?qū)ο笈c原型的講解购笆,算是理解了粗悯,在此做下筆記,僅為以后的個人學習作參考同欠。先從創(chuàng)建對象逐步剖析
這種Object構(gòu)造函數(shù)創(chuàng)建多個對象样傍,容易造成代碼冗余,所以有工廠模式的出現(xiàn)
但是工廠模式?jīng)]有解決多個對象識別的問題铭乾,即怎樣知道一個對象的類型,怎樣體現(xiàn)呢
很明顯娃循,box2是createObject的對象炕檩,但三個提示框的結(jié)果都是true,只知道它們都是object捌斧,但不知道是誰的對象笛质,所以就有了構(gòu)造函數(shù)創(chuàng)建對象模式的出現(xiàn)
注意幾點: 構(gòu)造函數(shù)沒有顯式地創(chuàng)建對象;直接將屬性和方法賦給了this對象捞蚂;要創(chuàng)建實例妇押,必須使用new 關(guān)鍵字;將構(gòu)造函數(shù)的作用域直接付給這個對象(this也就指向了這個新對象)姓迅;構(gòu)造函數(shù)的首字母必須大寫敲霍,比如例子中的Box,Box1丁存;
構(gòu)造函數(shù)的調(diào)用
構(gòu)造函數(shù)的問題(不同實例的同名函數(shù)不相等)
創(chuàng)建兩個完成同樣任務(wù)的函數(shù)沒有必要肩杈,但我們可以這樣來解決
我們把sayName()函數(shù)的定義轉(zhuǎn)移到構(gòu)造函數(shù)外部,在構(gòu)造函數(shù)內(nèi)容解寝,我們把sayName屬性設(shè)置成等于全局的sayName函數(shù)扩然,box1、box2對象就共享了在全局作用中定義的同一個sayName函數(shù)聋伦,這著實解決了兩個函數(shù)做同一個事情夫偶。但是新的問題又來了,sayName是全局函數(shù)觉增,如果對象需要用到多個函數(shù)兵拢,那么就要定義多個全局函數(shù),這樣這個自定義的引用類型沒有封裝性可言逾礁。我們可以通過原型模式來解決
使用原型的好處:讓所有對象實例共享它的屬性和方法
理解原型對象:只要創(chuàng)建了一個新的函數(shù)说铃,就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向原型對象。在所有默認的請款下截汪,所有的原型對象都會自動獲得一個constructor(構(gòu)造函數(shù))屬性,這個屬性包含一個指向prototype屬性所在函數(shù)的指針植捎。就拿上面的例子來說衙解,Box.prototype.constructor指向Box
用原型對象isPrototype()方法測試了Box,因為它們的內(nèi)容都有一個Box.prototype的指針济锄,因此都返回了ture
每當代碼讀取某個對象的某個屬性時暑椰,都會從實例開始,如果找到了話就返回荐绝,沒有找到則繼續(xù)搜索指針指向的原型對象一汽,在原型對象中查找具有給定名字的屬性。
可以通過對象實例訪問原型的值低滩,但是不能通過對象實例重寫原型中的值召夹,但是可以通過delete完全刪除實例屬性的值,從而使對象實例訪問原型的值
hasOwnProperty()可以檢測一個屬性是存在實例中還是存在原型中恕沫,存在實例中返回true
原型與in操作符(兩種使用情況:單獨使用和在for-in循環(huán)當中)
還有個值得注意的是hasprototypeProoerty()是與hasOwnprotery剛好相反
使用字面量來創(chuàng)建原型
原型的動態(tài)性
原型對象的問題:也是原型對象的優(yōu)點婶溯,即最大的問題是由共享屬性所導致的鲸阔。然而,如果原型當中包含引用類型的屬性來說迄委,問題就比較突出了
這里還是有個問題
兩張圖的區(qū)別是一個通過box1.family.push()褐筛,另一張是通過box1.family = [],結(jié)果不一樣,引用類型.屬性.方法可以重寫原型當中的值跑筝,但是引用類型.屬性只能操作但是不能修改原型中的值死讹,針對這些問題,所以有組合使用構(gòu)造函數(shù)和原型模式的出現(xiàn)
組合使用構(gòu)造函數(shù)和原型模式
動態(tài)原型模式
注意:使用動態(tài)原型模式赞警,不能使用對象字面量重寫原型;如果在創(chuàng)建實例的情況下虏两,那么就會切斷現(xiàn)有實例與原型之間的關(guān)系
寄生構(gòu)造函數(shù)模式
改函數(shù)的作用是僅僅是封裝創(chuàng)建對象的代碼愧旦,然后在返回新創(chuàng)建的對象。關(guān)于寄生構(gòu)造函數(shù)模式:返回的對象與構(gòu)造函數(shù)或者構(gòu)造函數(shù)的原型屬性之間沒有關(guān)系定罢;也就是說笤虫,構(gòu)造函數(shù)返回的對象與在構(gòu)造函數(shù)外部創(chuàng)建的蚃沒有什么不同。為此,不能依賴instanceof操作符來確定對象類型琼蚯。不建議使用這種模式
穩(wěn)妥構(gòu)造函數(shù)模式
咋一看酬凳,好像跟寄生構(gòu)造函數(shù)沒有什么不同。但還是有兩點不同的遭庶,一是新建對象的實例方法不引用this宁仔;二是不使用new操作符調(diào)用構(gòu)造函數(shù),Box里面的屬性是似有峦睡,除了run方法之外翎苫,沒有其他的辦法訪問name和age屬性,因此穩(wěn)妥構(gòu)造函數(shù)是比較安全的
繼承
繼承是OO(面向?qū)ο螅┱Z言一個重要的概念榨了。許多OO語言都支持兩種繼承方式:接口繼承和實現(xiàn)繼承煎谍。接口繼承則繼承方法簽名,函數(shù)沒有簽名龙屉,因為ECMAScript只支持實現(xiàn)繼承呐粘,而且實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn)的
原型鏈:利用一個引用類型繼承另一個引用類型的屬性和方法。構(gòu)造函數(shù)叔扼、原型和實例的關(guān)系:每個夠走啊函數(shù)都有一個原型獨享事哭,原型對象都包含一個指向構(gòu)造函數(shù)的指針,而實例都柏寒一個指向原型對象的內(nèi)部指針瓜富。
注意:有時我們需要在子類型中重寫超類型的某個方法鳍咱,或者需要添加超類型中不存在的某個方法,但不管怎樣与柑,給原型添加方法的代碼一定要在替換原型之后
還有一點谤辜,通過原型實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法价捧,因為這樣會重寫原型鏈
原型鏈的問題:1丑念、包含引用類型值得原型屬性會被實例共享(之前的解決辦法是使用構(gòu)造函數(shù)+原型,即引用類型值放在構(gòu)造函數(shù)當中而不是原型當中)结蟋,通過原型鏈繼承脯倚,被繼承的原型會成為繼承的原型的一個實例,進而原先的實例屬性會成為另外一個原型的實例屬性了)
2嵌屎、在創(chuàng)建子類型的實例時推正,不能像超類型的構(gòu)造函數(shù)中傳遞參數(shù)
借用構(gòu)造函數(shù)實現(xiàn)繼承
借用構(gòu)造函數(shù)的問題:方法都在構(gòu)造函數(shù)當中定義,函數(shù)復(fù)用就無從談起了宝惰。而且植榕,在超類型的原型中定義的方法,對子類型而言是不可見的
推薦:組合繼承(原型鏈+構(gòu)造函數(shù))
原型式繼承尊残,有點難以理解炒瘸,不想作過多的解釋
寄生式繼承(構(gòu)造函數(shù)+工廠模式,下面寫錯了)
使用集成式繼承為對象添加函數(shù)寝衫,會由于不能做到函數(shù)復(fù)用而降低效率顷扩;這一點與構(gòu)造函數(shù)模式類似
(常用)寄生組合式繼承
所謂寄生式繼承,通過借用構(gòu)造函數(shù)來繼承屬性慰毅,通過原型鏈的混成形式來繼承方法屎即。其基本的思路是:不必為了制定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無非就是hi超類型原型的一個副本而已事富。本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型乘陪,然后再將結(jié)果制定給子類型的原型统台。
結(jié)語,在面向?qū)ο笈c原型通過結(jié)合書本和視頻的方式去學習啡邑,本身原型和原型鏈是js的重點難點贱勃。希望在接下來的組件化開發(fā)當中能夠有效地運用起來,不然一切都是紙上談兵