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