9

I get an unexpected result with the following code:

var TestModel, u, u2;

function TestModel() {}
TestModel.prototype.a = null;
TestModel.prototype.b = [];

u = new TestModel();
u.a = 1;
u.b.push(1);

u2 = new TestModel();
u2.a = 2;
u2.b.push(2);

console.log(u.a, u.b);     // outputs: 1 [1,2]
console.log(u2.a, u2.b);   // outputs: 2 [1,2]

I find it surprising that u.b and u2.b contain the same values even though each instance of TestModel should have its own instance variables according to how I've setup the prototype. So this is the output I was expecting:

console.log(u.a, u.b);     // expecting: 1 [1]
console.log(u2.a, u2.b);   // expecting: 2 [2]

The same thing happens if I set b to be an object and set keys on it rather than using it as an array. What am I not understanding here?

3 Answers 3

13

There is a difference between assigning values and referencing them.

u.a = 1;

will create a new a property on the object referred to by u. Before the assignment, u.a will refer to TestModel.prototype.a, but assigning a new value actually creates a new property on the actual object:

enter image description here

After the assignment:

enter image description here

On the other hand,

u.b.push(1);

will not create a new property. It will reference the existing property, the array, which is TestModel.prototype.b.

even though each instance of TestModel should have its own instance variables according to how I've setup the prototype

All instances reference the same prototype, hence they reference the same properties the prototype has. You can easily see that because TestMode.prototype === u.b, TestMode.prototype === u2.b and u.b === u2.b all yield true.

It would work if you assign a new value as well to u.b and u2.b as well:

u.b = [];

which is normally done in the constructor:

function TestModel() {
    this.b = [];
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks a lot! I wasn't aware of that subtle difference. So basically I should initialise all my 'instance' variables in my constructor before manipulating them elsewhere.
@hiddentao: Exactly. It's ok to assign primitive types to prototype properties, as default values, but objects and arrays should be initialized in the constructor if they are "per instance". I normally still define the property on the prototype, but initialize it with null (like you did with TestModel.prototype.a).
@Felix, what tool were you using to get that nice chart of the values in TestModel?
@AresAvatar: It's just a screenshot of Chrome's Developer Tools console.
3

Prototype properties are exactly the opposite of each instance having their own variables, the point of prototype is that all instances automatically share the same prototype properties, so redefining functions is not required for each instance.

You want each instance to have their own array like this:

function TestModel() {
this.a = null;
this.b = [];
}

Comments

1

The array is in both cases the very same array, namely the one you set to the prototype. This means that both .push calls are acting on that array, so that all of these are [1, 2]:

TestModel.prototype.b
u.b
u2.b

They all refer to the same property.

Prototype is usually used for functions, so that all instances share those same functions. If you are going to modify prototype properties, then they will also be reflected for all instances, which in this case is probably undesirable. If instances should each have a custom array, then also declare a custom one for each instance instead of via prototype.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.