Создание нескольких экземпляров модуля

Я думал, что начинаю понимать 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.

Я был бы признателен, если бы кто-нибудь мог объяснить, как это работает, или указать мне в правильном направлении. Благодаря

Ответы

Ответ 1

Он остается 0, потому что вы не меняете переменную внутри Test, вы меняете объект, возвращаемый функцией. counter поддерживается "private", и только функция из 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.

Ответ 2

Вот что произошло:

  • функция init() сделала замыкание на переменной counter, которая определена внутри области проверки, содержащая ссылку на нее.
  • Возврат из функции Test() создал новый объект с другой переменной counter, установленный для значения внутреннего counter.
  • Вы обновляете это "другое" counter, устанавливая test1.counter = X, но init() по-прежнему содержит ссылку на исходную переменную.

Вот почему вы видите старое значение.

Ответ 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

В вашем примере вы не устанавливаете счетчик как свойство в свой тестовый объект/функцию, а при вызове test1.counter вы по существу устанавливаете новое свойство, которого раньше не было, а ваша функция init не ссылаясь на это свойство.

Как показывают ответные ответы dereks, вы, кажется, немного смутили два разных шаблона между тем, на что мой ответ следует, и его.

Ответ 4

Вот что я сделал бы:

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