2014-10-17 146 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

相关问题