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