4

Possible Duplicate:
Why are my JS object properties being overwritten by other instances

Why isn't the attribute "t" changed after setT was called? I would expect "4" as output, but it prints "default".

function Car(i) {
  var id = i;
  var t = "default";

  this.getT = function() { return t; }
  this.setT = function(p) {
    t = p;  // attribute t isn't changed ..
  }
}

function ECar(id) {  
  Car.call(this, id);  // super constructor call

  this.setT = function(p) {  // override
    ECar.prototype.setT.call(this, p); // super call
  }
}

ECar.prototype = new Car();

ecar = new ECar(3);
ecar.setT(4);
alert(ecar.getT()); // prints default, not 4
Community
  • 1
  • 1
user1789712
  • 51
  • 1
  • 4

3 Answers3

4

ECar.prototype = new Car();

At this line ECar's prototype get a context, in which all ECar's instance will be shared.

ECar.prototype.setT.call(this, p);

This line will call at that context, not what has been created while calling super at Car.call(this, id);.

You can fix your code with

function ECar(id) {  
  Car.call(this, id);  // super constructor call
  var carSetT = this.setT;
  this.setT = function(p) {
    carSetT.call(this, p);
  }
}

but it would be better (and more readable) to use real prototypes, such as

function Car() {}

Car.prototype.getT = function () { /* ... */ };
Car.prototype.setT = function () { /* ... */ };

function ECar() {}

ECar.prototype = new Car();
ECar.prototype.setT = function () { /* ... */ };

Edit: note (as @Bergi suggested)

You should only use Child.prototype = new Parent() as inheritance if you must support legacy browsers & then you should only use empty constructors.

The most (other-language) compatible way in JavaScript for inheritance is

Child.prototype = Object.create(Parent.prototype)

(MDN says it is supprted from IE 9)

pozs
  • 34,608
  • 5
  • 57
  • 63
2

// attribute t isn't changed ..

Please notice that t is not an "attribute", but a variable local to the constructors scope ("private").

ECar.prototype.setT.call(this, p); // super call

does not work how you expect it. You seem to want to change the variable created with the call to your super constructor (it's still local to that variable environment, and exposed by the getT and setT functions that were created in the constructor. So now, you are calling the function that was created in the line ECar.prototype = new Car(); - which changes the variable t that was created there. That you call the function on the current object does not matter, as it does not use the this keyword inside.

So, you don't want to a) use the method of that prototype Car, but your own and b) don't want to create an instance of Car for the prototype at all. See also What is the reason [not] to use the 'new' keyword here?. To apply the super constructor on your current instance is enough. If you want to extend the methods while still using the old ones, you need to preserve them (and exactly them) in a variable.

function Car(id) {
    var t = "default";
    this.getT = function () {
        return t;
    };
    this.setT = function (p) {
        t = p;
    };
}

function ECar(id) {
    Car.call(this, id); // super constructor call

    var oldSetter = this.setT;
    this.setT = function (p) { // override
        oldSetter(p); // call the function which access this instance's "t"
    }
}
ECar.prototype = Object.create(Car.prototype, {constructor: {value: ECar}});

var ecar = new ECar(3);
ecar.setT(4);
console.log(ecar.getT()); // prints 4
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0
function Car(i) {
   var id = i;
   var t = "default";

   this.getT = function() { return t; }
   this.setT = function(p) {
      t = p;  // attribute t isn't changed ..
   }
}

 function ECar(id) {  
     Car.call(this, id);  // super constructor call
 }

ECar.prototype = new Car();
ECar.prototype.constructor = ECar;  //Never forget doing this
ecar = new ECar(3);
ecar.setT(4);
alert(ecar.getT());

You don't need to override setT function. ​

emphaticsunshine
  • 3,725
  • 5
  • 32
  • 42
  • 3
    I think that was just simplified. He probably needs to override the function for another reason, but left it out to show that setT wasn't working. – numbers1311407 Oct 31 '12 at 21:19
  • then whats the purpose of using prototype? – emphaticsunshine Oct 31 '12 at 21:21
  • 1
    presumably he wants to do something else before or after calling the prototype method. Could be wrong, but otherwise yes, there's no reason to override the function. – numbers1311407 Oct 31 '12 at 21:22
  • As per my experience, all get and set methods are better attached to their prototype; saves a lookup as well. – emphaticsunshine Oct 31 '12 at 21:25
  • Sure, but what's the pattern for private members when defining functions on the prototype? – numbers1311407 Oct 31 '12 at 21:29
  • Please don't use `ECar.prototype = new Car();` at all – Bergi Oct 31 '12 at 21:39
  • @numbers1311407: functions on the prototype do not have access to any private variables - you need privileged functions (create in the same scope as the local variable) for that. Prototype methods can only interact with public properties (including methods) – Bergi Oct 31 '12 at 21:40
  • @numbers1311407, I thought the guy was adding setT and getT to Car's prototype. I just missed it. I was replying considering that. – emphaticsunshine Oct 31 '12 at 21:48