7

I created 2 instances of a prototype, changed a function in prototype, changes reflected in both the instances (Great). However, when I modified the prototype by removing the function, the function still existed for the existing instances.

function A() {
  this.name = "cool";
}

A.prototype = {
  howCool: function() {
    return this.name + "er";
  }
};

var a1 = new A(),
  a2 = new A();

a1.name = "hot";
//line1
console.log(a1.howCool());
//line2
console.log(a2.howCool());

A.prototype = {};

//line3
console.log(a1.howCool());

//line4
var a3 = new A();
console.log(a3.howCool());

Line 1 and 2 are working as expected and after setting the protoype back to empty, line 4 is showing undefined which is expected. line 3 however is still showing the function definition.

nem035
  • 34,790
  • 6
  • 87
  • 99
  • 6
    You're not modifying the prototype object, you're creating a new one. – Bergi May 03 '17 at 16:12
  • See also [the same confusion with `instanceof`](http://stackoverflow.com/q/14568239/1048572) and [how to properly define a Javascript prototype](http://stackoverflow.com/q/17474390/1048572) – Bergi May 03 '17 at 16:15
  • 2
    You actually reassigned prototype property to a new object. The old one still exists. – abhishekkannojia May 03 '17 at 16:15
  • 1
    FWIW, the reason why it doesn't work this way is the same as why after `var a = 42; var b = a; a = 21;`, `b` still has the value `42`: JavaScript as [*copy/assign by value*](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value) semantics. `b` has a copy of the value of `a`, not a reference to variable `a` itself. Similar with `prototype`, new instances get a copy of the *value* of `A.prototype` (which is a reference to an *object*), not a reference to the *property* `A.prototype` itself. – Felix Kling May 03 '17 at 17:11

4 Answers4

5

Essentially you're reassigning the prototype property for the function A to point to a new object. This does not affect the old object and thus doesn't affect prior instances.

Here's an illustration of what is happening.

After this code executes:

function A() {
  this.name = "cool";
}

A.prototype = {
  howCool: function() {
    return this.name + "er";
  }
};

var a1 = new A(),
  a2 = new A();

the situation is the following:

before

Then after this code executes:

A.prototype = {};

var a3 = new A();

The A.prototype points to a new object but the [[Prototype]] property for old instances still points to the old object.

after

If you want to actually remove the method, you must edit the original object, not point to a new one.

To actually remove howCool() method from the prototype, something like this would work:

delete A.prototype.howCool

Which would give:

finally

Now any future instances, such as a3, and the prior ones, would all still point to the same object but that object won't have the howCool() method.

nem035
  • 34,790
  • 6
  • 87
  • 99
1

Well if you modify the prototype it will affect the old instances, but if you create a new one it won't affect old instances.

So when you write:

A.prototype = {};
//line3
console.log(a1.howCool);

The object a1.__proto__ still points to the initial A.prototype, that's why it doesn't log undefined.

And then when you write :

//line4
var a3 = new A();
console.log(a3.howCool());

The new instance a3 is using the new created A.prototype that's why you got ùndefined`.

cнŝdk
  • 31,391
  • 7
  • 56
  • 78
0

Try using the delete operator

function A(){
    this.name = "cool";
}

A.prototype = {
    howCool : function(){
        return this.name + "er";
    }
};

var a1 = new A(),
    a2 = new A();

a1.name = "hot";
//line1
console.log(a1.howCool);
//line2
console.log(a2.howCool);

delete A.prototype.howCool;

//line3
console.log(a1.howCool);

//line4
var a3 = new A();
console.log(a3.howCool);
0

Here is the possible explanation:
(Note: Below explanation is only for experiments and conceptual understanding as usage of __proto__ should be avoided in production code)

As you know that there is a link between an object(in this case a1) and the prototype of the constructor function( in this case A) out of which object is created. This link is called __proto__ or dunder proto. So in this case it can be seen as a1.__proto__.

Now, what is happening is that when you say A.prototype = {};, at this time object a1 has already been constructed and it is pointing to A.prototype via this link.

Since Javascript is garbage collected language, merely setting A.prototype to {} isn't cleaning up the old protoytpe object right then and hence the older object is still lingering around. So when you say console.log(a1.howCool());, howCool is located in the lingering object.

So, if you modify your line3 like this:

//line3

console.log(a1.__proto__);
a1.__proto__ = {};
console.log(a1.howCool());

You would be able to see older prototype object and howCool() inside it in the console window in the line console.log(a1.__proto__); However, if you set a1.__proto__ = {}; then console.log(a1.howCool()); will not be able to locate the object and howCool() would not be found(as it is now set to empty object).

So just setting A.prototype to {} isn't achieving the same effect as a.__proto__ = {}.

Hence the output that you see.

Hope it helps.

Pankaj Shukla
  • 2,657
  • 2
  • 11
  • 18
  • Please don't use `__proto__`, it's deprecated and creates wrong impressions. Use the standard `Object.getPrototypeOf`. – Bergi May 03 '17 at 17:47
  • @Bergi Sure, I am not intending to use it. This is just to explain the reasoning behind the behavior OP is seeing. I just didn't want to confuse him with additional details. It is easier for him to see this property on a1 object in the console window. – Pankaj Shukla May 03 '17 at 18:00
  • Except that it *isn't* an actual property, therefore confusing imo. – Bergi May 03 '17 at 18:42
  • @Bergi Yes, you are absolutely right! However, I hope larger cause of explaining what is happening is not lost due to this usage. This is in no way meant to be written in production code, just for exploring and experimentation. – Pankaj Shukla May 03 '17 at 18:48