3
function Entity() {
    this.a = {a: 4};
    this.b = 5;
}

function Thing() {}
Thing.prototype = new Entity;

var thing1 = new Thing;
thing1.a.a = 3;
thing1.b = 4;
var thing2 = new Thing;
console.log(thing2.a.a); // prints 3 instead of 4. I would like 4.
console.log(thing2.b) // prints 5 as wanted

I'm having difficulty with setting prototypical inheritance in javascript. Ideally I want both thing1 and thing2 to have their own clean copy of the "new Entity prototype".

using this.__proto__ is something I want to avoid

[Edit]

I have a rough idea of how this working.

Setting thing1.b sets the b property on the Thing instance. which does not touch the Entity.b defined in the prototype chain.

Where as setting thing1.a.a on the Thing instance cannot been done because it would throw an "cannot set a on undefined" error. That's when it goes up the prototype chain to find Entity.a which is defined and sets Entity.a.a to a new value.

[Further Edit]

As @IvoWetzel says setting thing1.b does not touch the prototype chain because setting properties does not. Where as setting thing1.a.a does two steps. A getter on thing1.a which touches the prototype chain followed by a setter of .a

2
  • Can you use less confusing variable names please? Perhaps don't reuse a and b for both member and global variables. Try foo, bar, baz or something else. Commented Nov 24, 2010 at 0:18
  • I used something slightly more sensible. Probably wise. Commented Nov 24, 2010 at 0:21

3 Answers 3

6

A thing you could do is to apply the logic of your Entity constructor inside Thing, for example:

function Entity() {
    this.a = {a: 4};
    this.b = 5;
}
Entity.prototype.c = 6;

function Thing() {
  Entity.apply(this, arguments); // ejecutes the assignments made in Entity
}
Thing.prototype = new Entity;

var a = new Thing;
a.a.a = 3;

var b = new Thing;
console.log(a.a.a); // 3
console.log(b.a.a); // 4

console.log(a.b);   // 5
console.log(b.b);   // 5

console.log(a.c);   // 6
console.log(b.c);   // 6
Sign up to request clarification or add additional context in comments.

6 Comments

That solves the issue but doesnt explain why a.a.a references something inside new Entity object where as a.a & a.b reference something inside the new Thing object
If you consider that the OP is asking for prototypes to behave like classes, then it makes sense that the OP should be calling the superclass constructor, which is what this equates to.
It does, really; with .a.a, you're setting a value inside an object, so the object is shared, whereas with .b you're reassigning the value. It's standard by-reference and by-value stuff common in almost all languages. And calling super in that way is the proper way to do it.
With that, also be aware that in constructing Thing.prototype you are calling the Entity constructor; if it shouldn't be run twice, you should do something like this: function tmp(){};tmp.prototype=Entity.prototype;Thing.prototype = new tmp(). That's the technique used in common libraries as it avoids the constructor call while still getting the prototype chain right (again, because the .prototype stuff is by reference, not by value).
@Raynos: DON'T do Thing.prototype = Entity.prototype - then the prototypes will be the same object. That's why Thing.prototype = new tmp() is used. You don't want Thing.prototype.a = 1 to affect Entity.prototype.a!
|
2

While CMS has posted a solution, why does thing2.b return 5 and why does thing2.a.a resolve to the object?

var thing1 = new Thing;

// thing1 has no a, but the prototype has so a.a is essentially the a of Entity
thing1.a.a = 3;

// Sets b on thing1, setting does not go up the prototype chain(!)
thing1.b = 4;  

// that's what thing1 looks like
Thing {proto: Entity { 
                      a: { <--- gets resolved 
                          a: 3 <-- gets set
                      }, 
                      b: 5
              },
              b: 4 <-- gets set directly
      }


var thing2 = new Thing;

// thing2.a does not exist, so we look up the prototype and find a on Entity
console.log(thing2.a.a); 

// thing2.b also does not exists, so once again we look up the prototype to find b on Entity
console.log(thing2.b);

// that's what thing2 looks like
Thing {proto: Entity {
                      a: {
                          a: 3 <- gets resolved
                      },
                      b: 5 <- gets resolved
              }
      }

All the trouble is about JavaScript going up the prototype chain in order to find properties. But when you set properties it does not go up the chain.

1 Comment

Why does a.a.a not set a.a on Thing? Thats what im really wondering.
0

That's because JavaScript objects are always treated as references.

Therefore when you change the this.a object by doing a.a.a = 3; you're changing that one object in memory. New instances of Thing will reference that same object in memory because the Entity constructor is not called every time Thing is, and the this.a object remains the same.

I would put this.a outside the prototype chain, probably directly inside the Thing constructor. This would make it so that a new version of this.a is created in memory every time Thing is instantiated.

1 Comment

this.a is in the Entity constructor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.