《javascript高級程序設計》第六章 讀書筆記 之 javascript對象的幾種創建方式

一、工廠模式

工廠模式:使用字面量和object構造函數會有很多重複代碼,在此基礎上改進
圖片描述
解決了多個相似對象的問題,但沒有解決對象識別的問題(即怎樣知道一個對象的類型)

二、構造函數模式

圖片描述
與工廠模式創建對象的不同之處:沒有顯示創建對象,直接將屬性和方法賦給this對象,沒有return語句。默認return的是this對象。
構造函數本身也是函數,只是可以用來創建對象,所以借鑑自其他面嚮對象語言,構造函數始終應該以一個大寫字母開頭,非構造函數用一個小寫字母開頭,用於區別構造函數與其他普通函數。
new操作符創建對象實例,經歷4個步驟:
1、創建一個新對象
2、講構造函數的作用域賦給新對象(所以此時this指向這個新對象)
3、執行構造函數中的代碼(爲這個新對象添加屬性)
4、返回新對象
上面的實例a又一個constructor(構造函數)屬性指向Person。
圖片描述
相比較工廠模式的有點:有constructor屬相,可以標誌爲一種特性的類型。
但實際用來檢測對象類型,instanceod更方便些,所以instanceof比較的是constructor屬性。
三種使用構造函數創建對象的方法:
圖片描述

call和apply的作用都是在某個特殊對象的作用域中調用Person()函數。那麼上例中,otherPerson的作用域中調用Person函數,this指向otherPerson對象,執行構造函數中的內容,this.name指otherPerson.name=‘shirley’
構造函數模式的缺點:每創建一個實例都要執行一遍構造函數的語句,每個實例有不同的屬性值很正常,也有許多內容一模一樣的方法,這些方法重複創建(實際是new function實例,所以person1.sayname!=otherPerson.sayname。因爲sayName方法是不同實例,引用地址不同)則顯得冗餘。
很明顯,如果實例引用同一個sayName實例(同一個方法)就能夠解決上述問題。
圖片描述
Person的sayName引用的都是同一個sayName函數,即全局變量中的sayName函數。

三、原型模式

利用每個函數都有的一個prototype(原型)屬性。這個屬性是一個指針,指向一個對象,這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。(解決了構造函數模式的缺點)
prototype就是通過調用構造函數而創建的那個對象實例的原型對象。(就是所創建的對象的原型,例如:var person1=new Person(’linda‘,15)person1的原型爲Person。otherPerson=new Object() otherPerson的原型爲Object)
例子:
圖片描述
判斷一個對象的prototype是否指向某對象
圖片描述
訪問一個對象的prototype屬性
圖片描述
Firef、Safari、Chrome中支持一個屬性__proto__可以訪問到prototype
注意: JavaScript 中任意對象都有一個內置屬性 [[Prototype]] ,在ES5之前沒有標準的方法訪問這個內置屬性,但是大多數瀏覽器都支持通過__proto__來訪問。以下統一使用__proto__來訪問 [[Prototype]],在實際開發中是不能這樣訪問的。(摘自:https://segmentfault.com/a/11...
ES5中增加了一個新方法,Object.getPrototyOf()
圖片描述
“原型最初只包含constructor屬性”即,對象的constructor保存在原型屬性中。
對象實例不能改變原型中的值,所以當我們試圖改變原型中的值時,會在實例中新添加一個相同名稱的屬性。當我們訪問這個屬性時,就會優先返回實例中的屬性,這就是覆蓋了原型的屬性。
那麼我們怎麼確定一個屬性是來自對象實例還是來自對象的原型,使用Object對象的hasOwnProperty()方法可以檢測屬性是否存在於對象實例中,若存在,則訪問的屬性必定是來自對象實例,因爲當訪問一個屬性時,先搜索對象屬性若不存在纔會去搜索對象的原型屬性的。
圖片描述
delete可以刪除實例的屬性,但不能刪除對象原型中的屬性。
對象、構造函數(constructor)和原型(Prototype)的聯繫:
如下圖
圖片描述

person1和person2實例中的prototype屬性中保存的是Person的原型,原型裏有constructor屬性又指向Person。所以對象和constructor之間是通過原型聯繫起來的。

確定屬性是原型中的屬性:
in操作符只要通過對象能夠訪問到屬性就返回true,不管是實例的屬性還是原型中的屬性,所以通過in操作符返回true兒hasOwnProperty()返回false就可以確定屬性是原型中的屬性。

用字面量法定義Person的prototype屬性:
圖片描述
會造成constructor屬性不再指向Person
我們知道,沒創建一個函數,就會同時創建它的prototype對象,這個對象也會自動獲得constructor屬性。使用字面量定義prototype會重寫這個對象,因此constructor會變成新的對象的constructor屬性(指向object構造函數)。
打印person2顯示:
圖片描述
紅框內沒有constructor屬性。我們知道person2.constructor會尋找person2對象中的constructor對象中的constructor對象,person2實例對象中沒有,尋找原型中(紅框)也沒有,繼續尋找prototype的原型對象即object對象,所以,person2.constructor===Object true;
圖片描述
原型模式創建對象是在函數構造模式的基礎上解決共有方法和屬性的,那麼函數構造模式是爲了解決字面量法的無法判斷是哪個對象的問題而出現,怎麼判斷?通過對象的constructor屬性判斷,現在原型的constructor都指向對象,就把這個優勢抹去了,所以出現下面這樣的方法來保存這個特性。
圖片描述
這樣做與原來的差別是:constructor屬性的[[Enumberable]]特性被設置爲true,原來是false(不可枚舉)。所以如果使用兼容EXMAScript5的JavaScript引擎,可以使用Object.defineProperty()
圖片描述
原型具有動態性。
構造函數的原型改變會立即反映到所有實例中,但如果是重新寫一個原型對象則不會了

圖片描述
圖片描述
原因:person1的構造函數會自動指向一個原型對象,而保存的是這個原型對象的指針,新建一個prototype對象後,在堆內存中開闢了一個新的對象空間,在person2中保存的是這個新對象的引用指針,所以兩者不同。
原型模式很少會單獨使用,因爲所有屬性都是共享的,但實力一般都是要有屬於自己的全部屬性的。原型模式無法做到,所以這是原型模式的缺點。

四、組合使用構造函數和原型模式

創建自定義類型的最常見方式,構造函數模式用於定義實例屬性,原型模式用於定義方法和共享的屬性。
圖片描述
結果每個人實例都有自己的一份實例屬性的副本,同時又共享着對方法的引用,最大限度地節省了內存。這種方式還支持向構造函數傳遞參數。(既有構造函數模式的優點又有原型模式的優點)

五、動態原型模式

在構造函數中判斷是否存在sayName函數,如果不存在添加到原型中,下圖創建了兩次對象,調用兩次構造函數,但if的判斷只執行一次,在第一次執行。

圖片描述

六、寄生構造函數模式

與工廠模式在寫法上紅框圈出不同之外一模一樣。叫法上把Person函數叫做構造函數,其他無區別
圖片描述
適用情境:
可以在特殊的情況下用來爲對象創建構造函數。假如我們想創建一個具有額外方法的特殊數組。
圖片描述

七、穩妥構造函數模式

穩妥對象:沒有公共屬性,而且其方法也不引用this的對象。
穩妥對象最適合在一些安全的環境中(這些環境中會禁止是使用this和new),或者在防止數據被其他應用程序(如Mashup程序)改動時使用。
穩妥構造函數與寄生構造函數類似的模式。不同點:1、新創建對象的實例方法不引用this,2、不適用new操作符調用構造函數。

圖片描述
所以,使用穩妥構造函數模式創建的對象與構造函數之間沒有關係,無法用instanceod判斷所屬類型。
之所以穩妥,是因爲除了sayName方法外部無法對傳入構造函數中的原始數據進行訪問。即使可以給這個對象添加方法或數據成員。
使用環境:某些安全執行環境下使用(ADsafe和Caja)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章