JS面向對象的程序設計_建立對象之組合使用模式-2

前言

最近在細讀Javascript高級程序設計,對於我而言,中文版,書中不少地方翻譯的差強人意,因此用本身所理解的,嘗試解讀下。若有紕漏或錯誤,會很是感謝您的指出。文中絕大部份內容引用自《JavaScript高級程序設計第三版》。數組

1. 組合使用構造函數模式和原型模式

建立自定義類型的最多見方式,就是組合使用構造函數模式與原型模式。 安全

構造函數,用於定義實例對象的屬性。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中使用最普遍、認同度最高的一種建立自定義類型的方法。 能夠說, 這是定義引用類型的一種默認模式。==翻譯

2. 動態原型模式

有其餘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操做符肯定它的類型。

注意: 使用動態原型模式,不能使用對象字面量重寫原型。
若是在已經建立了實例對象的狀況重寫原型,那麼會切斷已經建立的實例對象與新原型之間的聯繫。

3. 寄生構造函數模式(不建議使用)

一般,在前述的幾種模式都不適用的狀況下,可使用(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操做符對這種對象也沒有意義。

相關文章
相關標籤/搜索