最近在細讀Javascript高級程序設計,對於我而言,中文版,書中不少地方翻譯的差強人意,因此用本身所理解的,嘗試解讀下。若有紕漏或錯誤,會很是感謝您的指出。文中絕大部份內容引用自《JavaScript高級程序設計第三版》。數組
建立自定義類型的最多見方式,就是組合使用構造函數模式與原型模式。 安全
構造函數,用於定義實例對象的屬性。app
原型模式,用於定義方法和共享的屬性。函數
這樣的話, 每一個實例對象都有屬於本身屬性的一份副本, 但同時又共享着對方法的引用,最大程度地節省了內存。this
這種混合模式還支持向構造函數傳遞參數, 可謂集兩種模式之長。google
//構造函數模式與原型模式, 應用示例 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實例對象分別引用不一樣的數組。prototype
==這種構造函數與原型模式混合使用的模式,是目前在ECMAScript中使用最普遍、認同度最高的一種建立自定義類型的方法。 能夠說, 這是定義引用類型的一種默認模式。==翻譯
有其餘OO語言經驗的開發人員在看到獨立的構造函數和原型時,極可能會感受到困惑。設計
動態原型模式正是致力於解決這個問題的一個方案,它把全部信息都封裝在了構造函數中。code
經過在構造函數中初始化原型(在一些必要的狀況下),又保持了同時使用構造函數和原型的優勢。
==換句話說,能夠經過檢查某個存在的方法是否有效, 來決定是否須要初始化原型。==
//動態原型模式示例代碼 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操做符來肯定對象的類型。
因爲存在上述問題,咱們建議在可使用其餘模式的狀況下,不要使用這種模式。
道格拉斯 克羅克福德(Douglas Crockford)famine了JavaScript中的穩妥對象(durable objects)這一律念。
所謂穩妥對象, 指的是沒有公共屬性,並且其方法也不引用this的對象。
穩妥對象最適合在一些安全的環境中(這些環境中會禁止使用this和new),或者在防止數據被其餘應用程序(如Mashup 程序)改動時使用。
穩妥構造函數,遵循與寄生構造函數相似的模式,但有兩點不一樣:
按照穩妥構造函數的要求,能夠將前面的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操做符對這種對象也沒有意義。