Javascript Module Pattern使用Prototype创建多个实例

时间:2014-10-17 14:50:05

标签: javascript jquery html html5 performance

我想要完成的任务:使用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"
    });

2 个答案:

答案 0 :(得分:5)

函数本身的属性表现得像static类成员(函数的属性,而不是它的实例)
function prototype的属性在各个实例中是不同的:

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" 

答案 1 :(得分:4)

你正在使用一个立即调用的函数表达式(IIFE),就像Module模式所说的那样,但对于这种情况,你需要不止一次调用你的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())()行中。与IIFE一样,moduleContext()函数为模块构造函数设置上下文,然后返回它。然后new运算符处理返回的函数,并在没有参数的情况下调用它(最后一组parens)。由于在IIFE上需要它们,所以需要围绕调用moduleContext()的额外问题:它们解决了JavaScript解析器中的一些歧义。

现在,您的两个模块在两个不同的上下文中创建。因此,您可以在任一模块中设置“common”选项对象(就像您当前那样),但只会影响该模块上下文中的options对象。另一个没有被触及,因此您可以单独设置选项。