Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript - Set properties in the constructor or constructor's prototype property?

So I've seen and heard that the methods should be set in the constructor's prototype property so it doesn't have several different instances. But what about properties themselves? Which is best practice? If that's the case, shouldn't constructor's always be empty?

function Gadget(name, color) {
     this.name = name;
     this.color = color;
     this.whatAreYou = function(){
       return 'I am a ' + this.color + ' ' + this.name;
     }
}

should this actually be...?

function Gadget(name,color){}

Gadget.prototype.name = name;
Gadget.prototype.color = color;
Gadget.prototype.whatAreYou = function() {
   return 'I am a ' + this.color + ' ' + this.name;
};
like image 713
KingKongFrog Avatar asked Apr 01 '13 17:04

KingKongFrog


2 Answers

If you set it on the prototype, the property is shared by all instances. Not usually what you want. I I blogged about this at http://js-bits.blogspot.com/2014/10/understanding-prototypical-inheritance.html . You usually want each Gadget to have their own name and color. How would you do that with the code you suggested?

By the way, the code you suggested has undefined variables (name, color)

The normal way is to set methods on the prototype, and regular values on the object itself. Unless you do want a property to be shared by all instances, that's like what we would call static properties in static typed languages.

Here's an example

function Gadget(name,color){
    this.name = name;
    this.color = color;
    // Since all gadgets in on the prototype, this is shared by all instances;
    // It would more typically be attached to Gadget.allGadgets instead of the prototype
    this.allGadgets.push(this);
    // Note that the following would create a new array on the object itself
    // not the prototype
    // this.allGadgets = [];

}

Gadget.prototype.allGadgets = [];
Gadget.prototype.whatAreYou = function() {
    return 'I am a ' + this.color + ' ' + this.name;
};

An important concept to remember is that writes (assignments) are always applied to the object itself, not the prototype. Reads, however, will traverse up the prototype chain looking for that property.

That is

function Obj() {
   this.map = {};
}

function SharedObj() {}
SharedObj.prototype.map = {};

var obj1 = new Obj();
var obj2 = new Obj();
var shared1 = new SharedObj();
var shared2 = new SharedObj();

obj1.map.newProp = 5;
obj2.map.newProp = 10;
console.log(obj1.map.newProp, obj2.map.newProp); // 5, 10

// Here you're modifying the same map
shared1.map.newProp = 5;
shared2.map.newProp = 10;
console.log(shared1.map.newProp, shared2.map.newProp); // 10, 10

// Here you're creating a new map and because you've written to the object directly    
// You don't have access to the shared map on the prototype anymore
shared1.map = {};
shared2.map = {};
shared1.map.newProp = 5;
shared1.map.newProp = 10;
console.log(shared1.map.newProp, shared2.map.newProp); // 5, 10
like image 128
Juan Mendes Avatar answered Oct 27 '22 20:10

Juan Mendes


For properties this may not behave the way you expect it to. Methods should be declared as on the prototype, because you want all instances to share the same function reference. Properties are usually different per instance. You might not notice this when dealing with primitives, but you will experience "weird" behavior for properties that are objects (or arrays, …)

function Test(){}
Test.prototype.foo = {bar: 1};
Test.prototype.baz = 1;

var one = new Test();
var two = new Test();
one.foo.bar = 3;
two.foo.bar === 3; // true, because of object reference
// BUT:
one.baz = 3;
two.baz === 1; // true
like image 41
rodneyrehm Avatar answered Oct 27 '22 21:10

rodneyrehm