摘要:組合使用構造函數模式和原型模式創建自定義類型的最常見方式,就是組合使用構造函數模式與原型模式。也就是說,寄生構造函數模式下,構造函數創建的對象與在構造函數外創建的對象沒有什么不同。
前言
最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaScript高級程序設計第三版》。
1. 組合使用構造函數模式和原型模式創建自定義類型的最常見方式,就是組合使用構造函數模式與原型模式。
構造函數,用于定義實例對象的屬性。
原型模式,用于定義方法和共享的屬性。
這樣的話, 每個實例對象都有屬于自己屬性的一份副本, 但同時又共享著對方法的引用,最大程度地節省了內存。
這種混合模式還支持向構造函數傳遞參數, 可謂集兩種模式之長。
//構造函數模式與原型模式, 應用示例 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ["Sharon", "Sandy"]; } Person.prototype = { constructor: Person, sayName: function(){ console.log(this.name); } } var person1 = new Person("Shaw", 28, "Designer"); var person2 = new Person("Roc", 27, "Doctor"); console.log(person1.sayName()); // "Shaw" console.log(person2.sayName()); // "Roc" person1.friends.push("Vans"); console.log(person1.friends); // ["Sharon", "Sandy", "Vans"] console.log(person2.friends); // ["Sharon", "Sandy"] console.log(person1.friends === person2.friends); // false console.log(person1.sayName === person2.sayName); // true
在這個例子中,實例對象的屬性都是在構造函數中定義的, 所有實例對象共享的屬性constructor和sayName()方法則是在原型中定義的。 修改person1.friends并不會影響到person2.friends, 因為person1和person2實例對象分別引用不同的數組。
==這種構造函數與原型模式混合使用的模式,是目前在ECMAScript中使用最廣泛、認同度最高的一種創建自定義類型的方法。 可以說, 這是定義引用類型的一種默認模式。==
2. 動態原型模式有其他OO語言經驗的開發人員在看到獨立的構造函數和原型時,很可能會感覺到困惑。
動態原型模式正是致力于解決這個問題的一個方案,它把所有信息都封裝在了構造函數中。
通過在構造函數中初始化原型(在一些必要的情況下),又保持了同時使用構造函數和原型的優點。
==換句話說,可以通過檢查某個存在的方法是否有效, 來決定是否需要初始化原型。==
//動態原型模式示例代碼 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; if(typeof this.sayName != "function") { Person.prototype.sayName = function() { console.log(this.name); } } } 4 var person1 = new Person("Shaw", 18, "Designer"); person1.sayName(); // "Shaw"
注意構造函數代碼中的這一部分。
if(typeof this.sayName != "function") { Person.prototype.sayName = function() { console.log(this.name); } }
沒有像原型模式一樣,顯式的定義原型的屬性和方法。而是調用構造函數時才會完全原型的初始化。
如:
function Person(){ } Person.prototype.sayName = function() { console.log(this.name); }
這段代碼只會在初次調用構造函數時才會執行。此后,原型完成初始化,不需要在做什么修改了。
不夠要記住,這里對原型所做的修改,能夠立即在所有實例對象中得到反映。
因此,這種做法可以說非常完美。
其中if語句檢查的可以是初始化之后,應該存在的任何屬性或方法- 不必用一大堆if語句檢查每個屬性和方法;
只需要檢查其中一個即可。
對于采用這種模式創建的對象,還可以使用instanceof操作符確定它的類型。
注意: 使用動態原型模式,不能使用對象字面量重寫原型。
如果在已經創建了實例對象的情況重寫原型,那么會切斷已經創建的實例對象與新原型之間的聯系。
通常,在前述的幾種模式都不適用的情況下,可以使用(parasitic)構造函數模式。
這種模式的基本思想是創建一個函數,該函數的作用僅僅是封裝創建對象的代碼,然后返回新創建的對象。
從表面上看,這個函數又很像是典型的構造函數。
function Person(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ console.log(this.name); } return o; } var friend = new Person("Shaw", 18, "Engineer"); friend.sayName(); // "Shaw"
在這個例子中,Person函數創建了一個新對象,并以相應的屬性和方法初始化該對象,然后返回了這個對象。
除了使用new操作符,并把使用的包裝函數叫做構造函數之外,這個模式跟工廠模式是一模一樣的。
構造函數在不返回值的情況下,使用new操作符,默認返回一個實例對象。
而通過在構造函數的末尾添加一個return語句, new操作符 + 構造函數 可以重寫返回的值。
這個模式可以在特殊的情況下用來為對象創建構造函數。
假設我們想創建一個具有額外方法的特殊數組。
由于不能直接修改Array構造函數, 因此可以使用這個模式。
function SpecialArray() { var values = new Array(); values.push.apply(values, arguments); values.toPipedString = function() { return this.join("|"); } return values; } var colors = new SpecialArray("red", "blue", "green"); console.log(colors.toPipedString()); //red|blue|green
在這個例子中,我們創建了一個名叫SpecialArray的構造函數。
在這個構造函數內部,首先創建了一個數組,然后push()方法(用構造函數接收到的所有參數)初始化了數組的值。
雖然又給數組添加了一個toPipedString()方法, 該方法返回以豎線分隔的數組值。
最后返回整個數組。( [arguments, toPipedString: ?]
接著,我們調用了SpecialArray構造函數,向其中傳入了用于初始化數組的參數。(["red", "blue", "green", toPipedString: ?])。
最后,調用了toPipedString()方法。
==關于寄生構造函數模式,需要注意:==
返回的對象與構造函數或構造函數的原型沒有關系。 也就是說,寄生構造函數模式下,構造函數創建的對象與在構造函數外創建的對象沒有什么不同。
所以不能依賴instanceof操作符來確定對象的類型。
由于存在上述問題,我們建議在可以使用其他模式的情況下,不要使用這種模式。
4. 穩妥構造函數模式道格拉斯 克羅克福德(Douglas Crockford)famine了JavaScript中的穩妥對象(durable objects)這一概念。
所謂穩妥對象, 指的是沒有公共屬性,而且其方法也不引用this的對象。
穩妥對象最適合在一些安全的環境中(這些環境中會禁止使用this和new),或者在防止數據被其他應用程序(如Mashup 程序)改動時使用。
穩妥構造函數,遵循與寄生構造函數類似的模式,但有兩點不同:
新創建對象的實例方法不引用this;
不使用new操作符調用構造函數。
按照穩妥構造函數的要求,可以將前面的Person構造函數重寫如下
function Person(name, age, job) { //創建要返回的對象 var o = new Object(); //可以在這里定義私有變量和函數 //添加方法 o.sayName = function() { console.log(name); } //返回對象 return o; } var friend = Person("Shaw", 18, "Designer"); friend.sayName(); // "Shaw"
注意,以這種模式創建的對象中,除了使用sayName()方法之外,沒有其他方法訪問name的值。
這樣,變量friend中保存的是一個穩妥對象。
即使有其他代碼會給這個對象添加方法或數據成員,但也不可能有別的辦法訪問傳入到構造函數中的原始數據。
穩妥構造函數模式提供的這種安全性,使得他非常適合在某些安全執行環境-例如,ADsafe(www.adsafe.org)和Caja (http://code.google.com/p/goog...) 提供的環境下使用。
與寄生構造函數模式類似,使用穩妥構造函數模式創建的對象與構造函數之間也沒有什么關系,因此instanceof操作符對這種對象也沒有意義。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98349.html
摘要:組合繼承最大的問題就是無論在什么情況下,都會調用兩次超類型構造函數一次是在創建子類型原型的時候。好在,我們已經找到了解決這個問題方法寄生組合式繼承所謂寄生組合式繼承,即通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法。 寄生組合式繼承 組合繼承是JavaScript最常用的繼承模式。 不過,它也有自己的不足。 組合繼承最大的問題就是無論在什么情況下,都會調用兩次超類型構造函數...
摘要:在基于原型的面向對象方式中,對象則是依靠構造函數和原型構造出來的。來看下面的例子優點與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約內存。 我們所熟知的面向對象語言如 C++、Java 都有類的的概念,類是實例的類型模板,比如Student表示學生這種類型,而不表示任何具體的某個學生,而實例就是根據這個類型創建的一個具體的對象,比如zhangsan、lisi,由...
摘要:不必在構造函數中定義對象實例的信息。其次,按照一切事物皆對象的這餓極本的面向對象的法則來說,類本身并不是一個對象,然而原型方式的構造函數和原型本身也是個對象。第二個問題就是在創建子類型的實例時,不能向超類型的構造函數中傳遞參數。 前言 對象(Object)應該算是js中最為重要的部分,也是js中非常難懂晦澀的一部分。更是面試以及框架設計中各出沒。寫這篇文章,主要參考與JavaScrip...
摘要:實現思路使用原型鏈實現對原型方法和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。繼承屬性繼承方法以上代碼,構造函數定義了兩個屬性和。 JS面向對象的程序設計之繼承的實現-組合繼承 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《Java...
摘要:第一種方式是使用操作符,只要檢測的實例對象中的原型鏈包含出現過的構造函數,結果就會返回。而這也正是組合使用原型模式和構造函數模式的原因。在構造函數模式中定義屬性,在原型模式中定義共享的方法。 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《Ja...
閱讀 1075·2021-11-22 14:56
閱讀 1520·2019-08-30 15:55
閱讀 3358·2019-08-30 15:45
閱讀 1655·2019-08-30 13:03
閱讀 2868·2019-08-29 18:47
閱讀 3334·2019-08-29 11:09
閱讀 2641·2019-08-26 18:36
閱讀 2615·2019-08-26 13:55