25

Has anyone noticed this behavior before? This really threw me off... I would have expected prototyped arrays to be private to each class instance rather than shared between all class instances.

Can someone verify that this is the correct behavior and perhaps explain this behavior in more detail?

Notice the commented code and how it affects the behavior of the script.

<html>
<head>
<script type="text/javascript">
function print_r( title, object ) {
 var output = '';
 for( var key in object ) {
 output += key + ": " + object[ key ] + "\n";
 }
 output = title + "\n\n" + output;
 alert( output );
}
function Sandwich() {
 // Uncomment this to fix the problem
 //this.ingredients = [];
}
Sandwich.prototype = {
 "ingredients" : [],
 "addIngredients" : function( ingArray ) {
 for( var key in ingArray ) {
 this.addIngredient( ingArray[ key ] );
 }
 },
 "addIngredient" : function( thing ) {
 this.ingredients.push( thing );
 }
}
var cheeseburger = new Sandwich();
cheeseburger.addIngredients( [ "burger", "cheese" ] );
var blt = new Sandwich();
blt.addIngredients( [ "bacon", "lettuce", "tomato" ] );
var spicy_chicken_sandwich = new Sandwich();
spicy_chicken_sandwich.addIngredients( [ "spicy chicken pattie", "lettuce", "tomato", "honey dijon mayo", "love" ] );
var onLoad = function() {
 print_r( "Cheeseburger contains:", cheeseburger.ingredients );
};
</script>
</head>
<body onload="onLoad();">
</body>
</html>

Many thanks.

asked Dec 13, 2010 at 2:10

3 Answers 3

38

The prototype of an object is just an object. The prototype properties are shared between all objects that inherit from that object. No copies of the properties are made if you create a new instance of a "class" (classes don't exist anyway in JS), i.e. an object which inherits from the prototype.

It only makes a difference on how you use the these inherited properties:

function Foo() {}
Foo.prototype = {
 array: [],
 func: function() {}
}
a = new Foo();
b = new Foo();
a.array.push('bar');
console.log(b.array); // prints ["bar"]
b.func.bar = 'baz';
console.log(a.func.bar); // prints baz

In all these cases you are always working with the same object.

But if you assign a value to a property of the object, the property will be set/created on the object itself, not its prototype, and hence is not shared:

console.log(a.hasOwnProperty('array')); // prints false
console.log(a.array); // prints ["bar"]
a.array = ['foo'];
console.log(a.hasOwnProperty('array')); // prints true
console.log(a.array); // prints ["foo"]
console.log(b.array); // prints ["bar"]

If you want to create own arrays for each instance, you have to define it in the constructor:

function Foo() {
 this.array = [];
}

because here, this refers to the new object that is generated when you call new Foo().

The rule of thumb is: Instance-specific data should be assigned to the instance inside the constructor, shared data (like methods) should be assigned to the prototype.


You might want to read Details of the object model which describes differences between class-based vs. prototype-based languages and how objects actually work.

Update:

You can access the prototype of an object via Object.getPrototypeOf(obj) (might not work in very old browsers), and Object.getPrototypeOf(a) === Object.getPrototypeOf(b) gives you true. It is the same object, also known as Foo.prototype.

Bergi
671k162 gold badges1k silver badges1.5k bronze badges
answered Dec 13, 2010 at 2:22
Sign up to request clarification or add additional context in comments.

7 Comments

Hmm... Yes, that makes sense. I made the mistake of expecting prototype to function like a class definition would. The confusing thing is that if Foo.prototype.intValue = 5, and you say b.intValue = 4, a.intValue still = 5. So it only actually applies to the class's object/array members.
Then again, if you say b.array = ['whatever'], then a.array doesn't change either. So, explicit assignments using the "=" operator overwrite prototyped properties on an instanced object.
@Dan: No, I also showed in my code that a.array = ['foo']; will not change the prototype. But in your case with the integer and in my case with the array, you are assigning a totally new value to a property. And if you do this, the property will be set on the actual object. But if you do a.array.push(1), you are not setting a property, you are just calling a method in the array contained in a.array. You are not changing the reference.
Yes, sorry, that is what I meant when I said "overwrite prototyped properties on an instanced object" - you're affecting the actual object in that case, and only that instance. Also, thanks for the link to the details of the object model. I will need to read through that with a fine toothed comb.
@Dan: Your second comment is absolut correct, I just could not express it so well ;)
|
1

The behaviour is correct. [] is tranlated to new Array() in runtime, but only one such array is ever created.

In other words, Obj.prototype = {...} is executed just like any other assigment.

answered Dec 13, 2010 at 2:16

Comments

-1

When you do var exp1 = new C(), JavaScript sets exp1.[[Prototype]] = C.prototype. When you then access properties of the instance, JavaScript first checks whether they exist on that object directly, and if not, it looks in [[Prototype]]. This means that all the stuff you define in prototype is effectively shared by all instances, and you can even later change parts of prototype and have the changes appear in all existing instances.

Thomas Fritsch
10.3k33 gold badges42 silver badges49 bronze badges
answered Nov 13, 2017 at 23:49

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.