2014-10-17 225 views
4

我想完成的任務:在javascript中使用原型創建模塊,以便用戶可以多次實例化模塊,每個模塊有不同的選項。使用原型創建多個實例使用原型創建多個實例

的問題:使用var my_module3 = new module();,然後嘗試設置選項與my_module3.init({ option: "value" });每次不改變對象時,它只會改變一次。

測試:使用console.log我們可以看到,它打印出帶有即使它們設置不同

Object {first: "Barry", second: "Larry", third: "Sam"} 
Object {first: "Barry", second: "Larry", third: "Sam"} 

這裏是我的jsfiddle完整的代碼相同的選項,這兩個對象:http://jsfiddle.net/11bLouc8/2/

 var module = (function() { 
     // default options 
     var options = { 
      first: "test", 
      second: "test2", 
      third: "test3" 
     }; 
     // take in useroptions and replace default options 
     var module = function(userOptions) { 
      if (userOptions != null && userOptions != undefined 
       && userOptions != 'undefined') { 
       for (var opt in options) { 
        if (userOptions.hasOwnProperty(opt)) { 
         options[ opt ] = userOptions[ opt ]; 
        } 
       } 
      } 
     }; 

     //prototype 
     module.prototype = { 
      init: module, 
      options: options 
     }; 

     return module; 

    })(); 

    // create a new instance 
    var my_module3 = new module(); 
    my_module3.init({ 
     first: "Mike", 
     second: "Lisa", 
     third: "Mary" 
    }); 

    // another instance 
    var my_module2 = new module(); 
    my_module2.init({ 
     first: "Barry", 
     second: "Larry", 
     third: "Sam" 
    }); 
+1

爲什麼你的代碼修改默認選項?您需要爲每個實例創建一個新對象! – Bergi 2014-10-17 15:01:10

+0

http://stackoverflow.com/questions/6504726/how-can-i-pass-parameters-to-a-module-pattern-to-override-default-values-privat – AntonB 2014-10-17 15:02:05

+0

那麼,你問具體覆蓋在您的實例之間共享的默認值。這不是你想要的嗎? – Bergi 2014-10-17 15:04:34

回答

4

函數的屬性本身的行爲類似static類成員(函數的屬性,而不是它的實例)0的個 屬性跨越實例是不同的:

function test(){}; 
test.prototype = { 
    constructor : test, 
    first : '', 
    second : '', 
    third : '', 
    init : function(f, s, t){ 
     this.first = f; 
     this.second = s; 
     this.third = t; 
     return this; 
    }, 
    formatToString : function(){ 
     return 'first : ' + this.first + ', second : ' + this.second + ', third : ' + this.third; 
    } 
} 
var t1 = new test().init(1, 2, 3); 
var t2 = new test().init(4, 5, 6); 
console.log(t1.formatToString());//outputs "first : 1, second : 2, third : 3" 
console.log(t2.formatToString());//outputs "first : 4, second : 5, third : 6" 
+0

我不確定你的意思是「功能的屬性」(OP在哪裏使用它們?);以及「函數原型」的屬性在不同實例之間有何不同「(這與它們應該是相反的)。你願意詳細說明嗎? – Bergi 2014-10-18 14:53:48

+0

在JS的OOP中沒有'class',這就是爲什麼我用'function'來描述在我的例子中通過調用'new test()'創建的'object'的原因,可能會更好說'class'和'class instance'。直接訪問'prototype'中定義的屬性(類成員)(在我的例子中爲't1.first',而不是't1.prototype.first')的行爲與'class instance'屬性類似,不像'static'' class'字段 – www0z0k 2014-10-20 07:02:17

+0

@ www0z0k是的,將它們描述爲類和實例並不困惑,即使它不是JavaScript中的一個類。 – 2014-10-20 12:51:53

4

您使用的立即調用的函數表達式(IIFE),喜歡的模塊圖案說,你應該,但這種情況下,需要調用你的IIFE不止一次。這涉及給它一個名字,以便您可以再次解決它,因此在技術上它不再是IIFE,但它的工作原理與IIFE相同。我會繼續調用它

當你調用一個函數,JavaScript的創建其中的變量和封鎖住在一個範圍內。這方面的生活,只要IIFE之外的任何內容有任何東西的引用在裏面。這就是傳統模塊模式使用IIFE的原因:您可以在IIFE的上下文中隱藏私有數據和函數。

但是,由於您只調用一次該函數,所有模塊實例共享相同的上下文。您正在將模塊選項存儲在options變量中,該變量是該共享上下文的一部分,而不是模塊的一部分,因此當您更新其中一個模塊的選項時,它將更新所有模塊中的選項。有時候,這就是你想要的,但不是你的情況。

你想要做的是爲每個模塊創建一個新的上下文。這意味着你需要帶上你的IIFE並且保留對它的引用,這樣你可以多次調用它:換句話說,它不再是一個匿名函數(甚至不一定是IIFE)。但這都是可行的。這裏是一個可能的解決方案:

var moduleContext = function() { 
    // default options 
    var options = { 
     first: "test", 
     second: "test2", 
     third: "test3" 
    }; 
    // take in useroptions and replace default options 
    var module = function(userOptions) { 
     if (userOptions != null && userOptions != undefined 
      && userOptions != 'undefined') { 
      for (var opt in options) { 
       if (userOptions.hasOwnProperty(opt)) { 
        options[ opt ] = userOptions[ opt ]; 
       } 
      } 
     } 
    }; 

    //prototype 
    module.prototype = { 
     init: module, 
     options: options 
    }; 

    return module; 

}; 

var my_module3 = new (moduleContext())(); 
my_module3.init({ 
    first: "Mike", 
    second: "Lisa", 
    third: "Mary" 
}); 
var my_module2 = new (moduleContext())(); 
my_module2.init({ 
    first: "Barry", 
    second: "Larry", 
    third: "Sam" 
}); 
console.log(my_module2.options, my_module3.options); 

的奇蹟發生在這兩個new (moduleContext())()線。 moduleContext()函數與IIFE類似,爲模塊構造函數設置上下文,然後返回它。然後new運算符對返回的函數進行處理,並且不帶任何參數(最後一組參數)進行調用。圍繞moduleContext()調用的額外parens需要與IIFE所需的相同原因:它們解決了JavaScript解析器中的一些模糊問題。

現在,您的兩個模塊創建在兩個不同的上下文中。因此,您可以在任一模塊中設置「常用」選項對象(就像您當前所做的那樣),但只有該模塊上下文中的選項對象纔會受到影響。另一個沒有被觸及,所以你可以分開設置你的選擇。

+0

如果要在每次創建實例時更改原型,使用原型的意義何在? http://stackoverflow.com/a/16063711/1641941 – HMR 2014-10-20 23:54:55

+0

我承認我發佈的解決方案不完全是慣用的或實用的。從這些觀點來看,接受的答案更好。我發佈了這個答案的原因有兩個:因爲它更接近於問題中給出的代碼,並且因爲它更清楚地說明了爲什麼代碼不會執行提問者要做的事情。 – 2014-10-21 13:04:20

+0

這很有道理,IIFE在模塊中被調用一次,所以模塊的任何「新」實例都已被調用,最後的模塊選項會覆蓋任何「新」實例中的用戶選項。 – AntonB 2014-10-21 13:52:46