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

Ant*_*onB 5 html javascript performance jquery html5

我想要实现的目标:使用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"} 
Run Code Online (Sandbox Code Playgroud)

这是我的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"
    });
Run Code Online (Sandbox Code Playgroud)

小智 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" 
Run Code Online (Sandbox Code Playgroud)


The*_*est 5

您正在使用立即调用的函数表达式 (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);
Run Code Online (Sandbox Code Playgroud)

神奇发生在这两new (moduleContext())()行中。该moduleContext()函数与您的 IIFE 一样,为模块构造函数设置上下文,然后返回它。然后new操作符处理返回的函数,并在不带参数的情况下调用它(最后一组括号)。需要围绕调用的额外括号的moduleContext()原因与 IIFE 上需要它们的原因相同:它们解决了 JavaScript 解析器中的一些歧义。

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