Creating Multiple Instances of a Module
Asked Answered
D

5

15

I thought I was starting to understand JavaScript quite well but clearly not. Let me explain my problem with an example. First I have the following module defined:

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

    function init() {
        alert(counter);
    }

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

I then create 2 instances:

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

Now I update the counter variable (as it is public) and do some alerts. So far so good.

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

Now finally I say the following:

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

This is the bit I don't understand. Why does this alert 0? I thought the first alert would be 5 and the second 10.

I'd appreciate if someone could explain how the above could works or point me in the right direction. Thanks

Dredge answered 9/2, 2013 at 22:15 Comment(1)
Scalar typed variables are passed by value rather than by reference. So return { counter: counter, ... } just copies current var counter value and changing this.counterdoes not affect var counter.Excise
T
13

It stays 0 is because you are not changing the variable inside Test, you are changing the object returned by the function. counter is kept "private" and only a function in Test can access it.

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
    }
};

Now it will work: 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

For more information, see JavaScript Closures.

Thrash answered 9/2, 2013 at 22:22 Comment(1)
And why could be Test on global space available for all the scripts?Timtima
C
5

This is what happened:

  1. the init() function made a closure on the counter variable, which is defined inside Test scope, holding a reference to it.
  2. the return from the Test() function created a new object, with another variable counter, set to value of internal counter.
  3. You update that 'another' counter, by setting test1.counter = X, but the init() still holds a reference to original variable.

That's why you see the old value.

Caspar answered 9/2, 2013 at 22:28 Comment(1)
Thanks for the extra clarification. I awarded Derek the answer simply because his came before yours and it equally helped me out.Dredge
B
3

I am not sure if you have made a mistake in your post, but you could rewrite the above code as follows

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

In your example you don't set the counter to be a property on your Test object/function, rather when you call test1.counter you are essentially setting a new property which didn't exist before, and your init function is not referencing that property.

As dereks answer shows, you seem to have slightly confused the two different patterns between the one my answer follows, and his.

Bury answered 9/2, 2013 at 22:21 Comment(0)
G
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
Gunmaker answered 5/3, 2020 at 14:48 Comment(0)
E
0

Here is what I would do:

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
Entomb answered 25/9, 2016 at 0:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.