创建一个模块的多个实例

15

我曾经以为我已经很好地理解了JavaScript,但显然并没有。让我用一个例子来解释我的问题。首先,我定义了以下模块:

var Test = function() {
    var counter = 0;

    function init() {
        alert(counter);
    }

    return {
        counter: counter,
        init: init
    }
};

我随后创建了2个实例:

var test1 = new Test();
var test2 = new Test();

现在我更新计数器变量(因为它是公共的),并进行一些警报。到目前为止一切都很好。

alert(test1.counter); // Alerts 0
test1.counter = 5;
alert(test2.counter); // Alerts 0
test2.counter = 10;
alert(test1.counter); // Alerts 5

现在我最后说:

test1.init(); // Alerts 0
test2.init(); // Alerts 0

我不理解的是为什么会弹出0?我以为第一个弹出的应该是5,第二个是10。

如果有人能够解释一下上述代码是如何工作的,或者指导我方向的话,我将不胜感激。谢谢!


4
标量类型的变量通过值传递而不是引用传递。因此,return { counter: counter, ... } 只是复制了当前 var counter 的值,并且更改 this.counter 不会影响 var counter - Rango
5个回答

13

0保持不变是因为你没有改变Test内部的变量,而是改变了函数返回的对象。counter被保留为“私有”,只有Test中的函数可以访问它。

var Test = function() {
    var counter= 0;

    function init() {
            alert(counter);
    }
    function changeNum(n){
        counter = n;            //add a function inside `Test` so that it can
    }                           //access the variable

    return {
        counter: counter,
        init: init,
        changeNum: changeNum
    }
};

现在它会工作:http://jsfiddle.net/DerekL/pP284/

var test1 = new Test();
alert(test1.counter);           //0
test1.init();                   //0
test1.changeNum(5);
alert(test1.counter);           //5
test1.init();                   //5

了解更多信息,请参见JavaScript 闭包


为什么全局空间中的测试可以对所有脚本都可用? - Antonio Romero Oca

5
这是发生的事情:
1. init()函数在Test作用域内定义了counter变量,并将其引用保存在闭包中。 2. Test()函数的返回值创建了一个新对象,其中包含另一个名为counter的变量,其值设置为内部counter的值。 3. 你通过设置test1.counter = X来更新那个“另一个”counter,但init()仍然保持对原始变量的引用。 因此你看到的是旧值。

感谢您的额外解释。我选择授予 Derek 答案,仅仅是因为他的回答比您的早,并且同样对我有帮助。 - nfplee

3
我不确定您的帖子中是否有错误,但您可以将上面的代码重写为以下内容。
var Test = function() {
  this.counter = 0;
}

Test.prototype.init = function() {
  alert(this.counter);  
}

var test1 = new Test();
var test2 = new Test();


test1.counter = 5;
test2.counter = 10;

test1.init(); // alerts 5

test2.init(); // alerts 10

在你的示例中,你没有将计数器设置为Test对象/函数上的属性,而是当你调用test1.counter时,实际上是在设置一个之前不存在的新属性,而你的初始化函数没有引用该属性。
正如dereks的答案所示,你似乎有点混淆了我的答案所遵循的模式与他的答案之间的两种不同模式。

1
alert(test1.counter); // Alerts 0 ????? It's "0" because you call it before change counter to 5
test1.counter = 5;
alert(test2.counter); // Alerts 0 ????? It's "0" because you call it before change counter to 10
test2.counter = 10;
alert(test1.counter); // Alerts 5 | It's 5 because you call it AFTER change counter to 5

0

这是我会做的:

function Test() {
    this.counter = 0;
    this.init = function() { console.log(this.counter); }
};

var test1 = new Test();
var test2 = new Test();

console.log(test1.counter); //0
test1.counter = 5;
console.log(test2.counter); //0
test2.counter = 10;
console.log(test1.counter); //5

test1.init(); //5
test2.init(); //10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接