0

I'd like to know why if I pass an object of arguments to my constructor, the setting property values get overwritten by subsequent instantiations.

This doesn't happen when I pass arguments as separate vars to the constructor.

Example here: http://jsfiddle.net/KJ4CU/ and below

// First class
function Person(args) {
    for(s in args) {
       this.arguments[s] = args[s];   
    }
}

Person.prototype.arguments = {
    "gender" : null,
    "name" : null
};

Person.prototype.getGender = function() {
    return this.arguments.gender;
};

// Second class
function Animal(gender, name) {
    this.gender = gender;
    this.name = name;
}

Animal.prototype.getGender = function() {
    return this.gender; 
};

var p1 = new Person({ gender : "gal", name : "Jane"});
var p2 = new Person({ gender : "boy", name : "John"});
var a1 = new Animal("female", "Tina");
var a2 = new Animal("male", "Toto");

document.getElementById("p1").innerHTML = p1.getGender(); // boy
document.getElementById("p2").innerHTML = p2.getGender(); // boy
document.getElementById("a1").innerHTML = a1.getGender(); // female
document.getElementById("a2").innerHTML = a2.getGender(); // male
  • 1
    The `prototype` property is a shared property. Why are you using it if you want each instance to have its own? – Ian Dec 11 '13 at 05:40
  • 1
    Instance specific code *always* goes in the constructor. If you want that every instance has their own `arguments` object, you have to create the object in the constructor. – Felix Kling Dec 11 '13 at 05:43
  • I just want to know why prototype.settings.gender gets overwritten and prototype.gender does not. – user3062896 Dec 11 '13 at 05:46
  • 1
    Because you mutate prototype.settings and not re assign it as you do with prototype.gender. Even if the second class had a prototype.gender it would be shadowed. For more information check out this answer: http://stackoverflow.com/a/16063711/1641941 – HMR Dec 11 '13 at 05:48

2 Answers2

0

This is because the object arguments is a property of the prototype of your object:

Person.prototype.arguments = {
    "gender" : null,          
    "name" : null              
};

When you instanciate p2 = new Person(), the prototype is shared with p2, and the constructor is called:

function Person(args) {          // Constructor
    for(s in args) {             // Executed for each call to 'new Person()'
       this.arguments[s] = args[s];   
    }
}

this.arguments is resolved as this.prototype.arguments, which points to Person.prototype.arguments: it is shared, it is the same object.

Hence when you modify p2.arguments.name, you modify p1.arguments.name.

Instead of using the prototype, try adding an arguments properties to Person in the constructor.

function Person(args) {          // Constructor
    this.arguments = {           // Executed for each call to 'new Person()'
        gender: args.gender,     
        name: args.name
    };
}

This way, this.argument is a new object every time you create a new Person.

From this answer:

In class-based systems, the methods (not the properties/fields) are added to the prototype. Whereas an object's fields are instance-specific and therefore added to the object itself during construction.

For more information on javascript prototypes, see also this article.

Community
  • 1
  • 1
Aurélien Gasser
  • 3,043
  • 1
  • 20
  • 25
  • 1
    Funny, the link provided in your answer is pretty good but the answer with 739 is wrong. Inheritance by setting prototype of Child to an instance of Parent and not re using Parent constructor function in Child by doing `Parent.apply(this,arguments);` is a beginners attempt at using prototype at best. Not ashamed to admit that I made the same mistakes when starting out but the amount of upvotes does reflect how many JS programmers understand how prototype works. – HMR Dec 11 '13 at 06:44
-1

Your Animal class is how you should do it, I would stick with passing one object as arguments as it makes it more readable. In the constructor you can choose what to do with missing arguments (set a default or throw an error).

Passing one argument object makes it easier if you inherit as well: Parent.call(this,args);

function Animal(args) {
    this.gender = args.gender||"boy";//defaults to boy
    //make name mandatory
    this.name = args.name||false;
    if(this.name===false)
      throw new Error("Can't create an Animal without passing a name");
}
Animal.prototype.getGender = function() {
    return this.gender; 
};
var p1 = new Animal({ gender : "gal", name : "Jane"});
var p2 = new Animal({ gender : "boy", name : "John"});
HMR
  • 37,593
  • 24
  • 91
  • 160
  • @downvoter http://stackoverflow.com/help/privileges/vote-down "Use your downvotes whenever you encounter an egregiously sloppy, no-effort-expended post, or an answer that is clearly and perhaps dangerously incorrect." I don't think this answer applies to those criteria. – HMR Dec 12 '13 at 23:33