7

I have only just started messing up with Javascript inheritance and can't get my hed round this one:

If I run this code:

function Foo(y) {
    this.y = y;
}

Foo.prototype.x = 1;

var Bar1 = new Foo(2);
var Bar2 = new Foo(3);

I expect to have the following "structure" in memory: enter image description here I messed up in the graphic, Bar2 obviously has a value of "3" for its property "y"

And happily enough, I can confirm that, by running this code:

console.log("Prototype - x: ", Foo.prototype.x, " y: ", Foo.prototype.y);
console.log("Bar1 - x: ", Bar1.x, " y: ", Bar1.y);
console.log("Bar2 - x: ", Bar2.x, " y: ", Bar2.y);

which prints:

Prototype - x: 1 y: undefined
Bar1 - x: 1 y: 2
Bar2 - x: 1 y: 3

Correct me if I'm wrong but what is happening there is that when I try to access the property x in the Bar1 and Bar2 objects, as those objects don't localy have a property called x, I'm getting that property from the next object in the prototype chain; i.e. the one whose reference they store in their "_ proto _" property.

Now is when I get lost, because if I after that code I alter the value of x like this:

Bar1.x = 10;

when I now run

console.log("Prototype - x: ", Foo.prototype.x, " y: ", Foo.prototype.y);
console.log("Bar1 - x: ", Bar1.x, " y: ", Bar1.y);
console.log("Bar2 - x: ", Bar2.x, " y: ", Bar2.y);

what I'am getting is

Prototype - x: 1 y: undefined
Bar1 - x: 10 y: 2
Bar2 - x: 1 y: 3

instead of what I would expect :

Prototype - x: 10 y: undefined
Bar1 - x: 10 y: 2
Bar2 - x: 10 y: 3

At that moment I could only explain that by assuming that each object was creating a copy of the Foo.prototype object, but if I run this

console.log(Object.is(Foo.prototype, Bar1.__proto__), Object.is(Bar1.__proto__, Bar2.__proto__));

I get true true, so both Bar1, Bar2 are accesing the same object.

Why is Bar1 then showing a different value for x if they are both getting it from the same object?

Ruben Serrate
  • 2,724
  • 1
  • 17
  • 21
  • Why would you expect `Bar2` to get the value `10`, when it has only been placed on the `Bar1` object? Certainly we wouldn't want modifications to one object to affect *all* objects that share the same prototype, right? *"...I thought that each object was creating a copy of the Foo.prototype..."* There's no copy created when you edit. You merely edit the object. – cookie monster Aug 22 '14 at 15:41
  • ...to comprehend it, all you need to do is change your graphic to add `x 10` to the `Bar1` object, and that will reflect the entirety of what has taken place. – cookie monster Aug 22 '14 at 15:44
  • ...however, if you have an `Array` or an `Object` on the prototype, and you modify *that* object, naturally its changes will be observed from all objects that access it. `Foo.prototype.arr = []; Bar1.arr.push("foobar"); Bar2.arr.toString(); // "foobar"` – cookie monster Aug 22 '14 at 15:48
  • @cookiemonster Good point in the last comment! I understand how it works now, cheers! – Ruben Serrate Aug 22 '14 at 16:13
  • It's called shadowing assignment won't modify the prototype, only mutating will: http://stackoverflow.com/a/16063711/1641941 It's same as passing object variables to a function. If the function does `passedObject={}` it doesn't affect the object passed to the function but if it does `passedObject.something=22` it mutates passedObject and will affect the object outside of the function. – HMR Aug 22 '14 at 16:45

3 Answers3

3

This line:

Bar1.x = 10;

does not change the value of x in the prototype. Instead it creates a new property (x) for Bar1 and assigns the value of 10 to it. Bar1 therefore no longer inherits x from its prototype as it now has its own property x.

Bar2 still does, which is why it still matches the prototype value.

pete
  • 24,141
  • 4
  • 37
  • 51
2

It's pretty simply:

Assignment always creates or updates the property on the object itself.

An assignment never impacts the prototype chain, even if the property exists somewhere along the chain(*).

Only reading a property traverses along the prototype chain, stopping at the object that actually has that property.

If you are interested in the technicalities, have a look at the PutValue definition in the specification.


(*): However, if the property exists in the prototype chain, it may have an impact on the assignment. If the property exists and is readonly (not writable), the assignment doesn't create a new property. Example:

> var obj = Object.create(Object.create({}, {x: {value: 10}}));
undefined
> obj.x
10
> obj.x = 100;
100
> obj.x
10
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
0

You're right about how Bar1 and Bar2 are getting their values of x. But where it breaks down is when you assign Bar1.x = 10.

When trying to access x, it first starts at the object itself before crawling down the chain. Let's assume you try to access Bar1.z. The lookup would look something like this:

  1. Bar1.z
  2. Bar1.prototype.z
  3. Bar1.__proto__.z i.e. Foo.prototype
  4. Object.prototype.z (since all objects naturally inherit from Object
  5. z not found, returns undefined

By assigning Bar1.x, you effectively shadow the value of Bar1.__proto__.x since it will appear first in the lookup.

Mike Cluck
  • 31,869
  • 13
  • 80
  • 91