1

I have been getting into Javascript lately and thought I had figured out the prototype feature until I ran into a little problem.

I found out if you create two new instances of an object and in any of those two objects you change the value of a variable it changes in the other object too and this was not the effect I was looking for at the time and had to put the properties in the constructor instead to keep them separate in each instance.

When would it be desired to have each object instance sharing properties(strings, bools etc)?

Are prototypes only really used for declaring functions? and you declare all your properties in the constructor?

An example:

function Animal(name) {
    this.name = name;
}

Animal.prototype = {
    color: null,
    walk: function() {}
};

var dog = new Animal('Sam');
var horse = new Animal('Bob');

dog.color = 'black';
horse.color = 'chestnut';

The "dog" objects color is now 'chestnut' because that property is in the prototype and shared between both instances. Where would this effect be desired? At first glance all I can see from this is debugging headaches or is there a good use for this in Javascript? Sort of reminds me of bad global changes.

If the majority of the time you don't want to change a property in one instance and it also changes in all other instances do you just use prototypes for declaring functions?

6
  • 6
    There is probably something wrong with what you're doing, but with no code we can't tell. Please show us what you're doing! Commented Sep 6, 2013 at 22:00
  • Think public static int foo; in Java Commented Sep 6, 2013 at 22:06
  • @duskwuff Put some code in now. Commented Sep 6, 2013 at 22:12
  • 1
    What are you talking about imgur.com/ZcqTzCU Commented Sep 6, 2013 at 22:19
  • @David: I'm guessing the actual scenario where you experienced the problem was with putting an Object or Array on the .prototype. Generally, functions and primitive values are safe. The changes won't be reflected across all instances. But with Objects, any changes made to properties of the object will be seen because objects are held by reference, and are mutable. Commented Sep 6, 2013 at 22:22

2 Answers 2

4

You should not assign variables in the prototype (actually You are overwriting Animal.prototype with custom object). To define instance properties define them in constructor function like this:

function Animal(name) {
    this.name = name;
    this.color = null;
}

// Instance method
Animal.prototype.walk = function() {};

// Static method
Animal.run = function() {};


// Inheritance example
function Dog(name) {
    // Call parent constructor
    Animal.apply(this, arguments);
}

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.walk = function() {
    // Call parent method (if you need)
    Animal.prototype.walk.apply(this, arguments);
    ...
};
Sign up to request clarification or add additional context in comments.

Comments

4

With all due respect, you misunderstood how the prototype's behaviour.
Explaining everything would be too long a story, but concerning properties set on the prototype, it would be indeed a infinite source of bug if changing a property on an instance was changing this value for all instances.
Have a look, using jsfiddle, jsbin, cssdeck or whatever test site that you like, on your horse/dog example : the color of the dog is NOT changed when you change the horse's color. It is still black.

Why is that so ?
When an instance of dog does a READ access on a property, then it will be seeked on the prototype, then on the prototype's prototype, and so on until we reach the end of the prototype's chain. On the first value found, it will be returned, and if no property is found on no prototype, undefined is returned.
So right after an instance's creation, all animal colors will be null.
Now if you do a WRITE access on an instance's property, a brand new property will get created on this instance, and assigned the provided value. This way the prototype keeps its reference function, and the instance gets its value. Relief.

Still it is useful to define properties on the prototype, since :
1) you ensure all instances will provide a valid value for those properties -a default value-.
2) you can save memory by assigning once a property (and more importantly : a big object) that is shared amongst all instances (static). Expl : the default image for a dog.
3) you might want in some cases to change the values for all instances at once. In this case, changing the value on the prototype will do just that.
4) you allow the javascript interpreter to optimize your code by associating a class (in the C++ classic meaning) to your javascript class : when you assign a property existing on the prototype, the class is not 'broken', and the js interpreter can carry on using the background class to represent the instance. If performance matters, it is a very important point. If not, just forget about it :-)

So just a small example : if you define an animal, then each instance has its color and name : it make sense to have them set right in the constructor, and not on the prototype. But for the leg count for instance, it will not change from an instance to another, and if you create child classes, you can always change the value on the children's prototype to have a children class change this count :

function Animal(name, color) {
    this.name = name;
    this.color = null;
}    
Animal.prototype.legCount = 4; // most animal have 4 legs (...)

// Dog Class, inheriting from Animal
function Dog(name) {    Animal.apply(this, arguments);    }
// Set Animal as Dog's prototype's prototype.
// so that we can safely change Dog's prototype.
Dog.prototype = Object.create(Animal.prototype); 

// Duck class
function Duck(name) {    Animal.apply(this, arguments);    }
// same inheritance scheme
Duck.prototype = Object.create(Animal.prototype);
// ... but we change the legCount, for the ducks only
Duck.prototype.legCount = 2;

Advanced answer :

If you want a prototype property to remain unchanged even if we try to assign a value to this property on an instance, set it as a readonly property on the prototype.

Object.defineProperty(Duck.prototype,'legCount', { get : function() { return 2 } } );

var duck = new Duck(...) ;
duck.legCount = 5;
console.log (duck.legCount) ; // --> output is still 2

If you want that setting the value on an instance change the value for all instances (without explicitely change the prototype's property value), then have it done in the setter of the prototype's property definition.

 // using a closure
 var duckLegCount = 2;
 Object.defineProperty(Duck.prototype,'legCount', { 
                            get : function()   { return duckLegCount } ,
                            set : function (x) { duckLegCount = x    }    } );
 var duck1 = new Duck(...);
 var duck2 = new Duck(...);
 duck1.legCount = 12;
 console.log ( duck2.legCount ) ;  // output is 12

1 Comment

Best answer ever. Was looking to understand deeply, and that helped me way more than anything I've read. also, question: For your 3rd point (changing all values of instances), I guess it is only valid if you didn't redefined it at the object level (as it won't reach the prototype), right?

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.