Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript __proto__ output

Below are some examples showcasing the different behaviors of prototypal inheritance based on how the objects are defined and created. I distinguish between the "prototype property" of an object, e.g. someObject.prototype and the "prototype reference" (which I think should refer to the object from which someObject inherits?).

Example 1

This seems to be the way to preserve the parent object's prototype property. Isn't this the recommended way of inheriting?

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);
Parent.a = "1";
Parent.f = function() { return true; };

// add stuff to prototype property
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

console.log(Parent.__proto__)  // [Function: Object]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // { a: '1', f: [Function] }
console.log(Child.prototype)   // { b: 1, g: [Function] }
  1. I would have expected Child.__proto__ to be something naming Parent in the same way Parent.__proto__ names Object.

  2. We see that Child's prototype reference points to the properties of Parent rather than the properties of Parent.prototype. This is counterintuitive, to me at least, as I would have expected to see Parent.prototype's properties b and g instead.

Example 2

Mixed results.

// use a constructor instead.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// again, add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

// create differently
var Sibling = Object.create(Parent);

console.log(Parent.__proto__)   // [Function: Empty]
console.log(Parent.prototype)   // { b: 1, g: [Function] }

console.log(Child.__proto__)    // { b: 1, g: [Function] }
console.log(Child.prototype)    // undefined

console.log(Sibling.__proto__)  // [Function]
console.log(Sibling.prototype)  // { b: 1, g: [Function] }
  1. Here, Child's prototype references Parents.prototype's properties. This is what I expected above?

  2. On the other hand, Sibling's prototype reference is now a function, which is the prototype reference of Parent.

Example 3

This seems to be the way to preserve the parent object's prototype reference, but you lose its prototype property.

// create object constructor; add stuff.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = function() {
    this.c = "2";
};

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

Is the method shown in Example 1 preferred since you have access to both the parent's prototype property, its own properties as well as Object's properties/methods? I've read in other posts that some of these ways of inheriting should be equal...which is not true. Also, I've read other posts, e.g. here, that Example 3 is the way to go. Or perhaps I am just not sure what __proto__ stands for...

Thanks for any clarification you might have for the differences between these mix and match situations!

like image 214
jayflo Avatar asked Jan 20 '26 07:01

jayflo


1 Answers

I distinguish between the "prototype member" of an object, e.g. someObject.prototype and the "prototype reference"

Good terminology, and distinguishing them is the first step in understanding them and the difference between them.

(which I think should refer to the object from which someObject inherits?).

Exactly. In contrast to the .prototype member, you acess the protypte reference via Object.getPrototypeOf(obj) or the non-standard .__proto__ property.

Example 1

Isn't this the recommended way of inheriting?

It's one way to do inheritance, and the most pure form of prototypical inheritance. Notice that Parent and Child are plain objects, no constructor functions are involved.

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);

That's the first mistake: You're inheriting from the Object function (which is based on the constructor pattern, see below). You should just do

var Parent = {};
// or
var Parent = Object.create(Object.prototype);
// add stuff to prototype member
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

Yet, the .prototype member is of no significance in this model of inheritance. It's just a normal property, in this case one inherited from Object. You're actually modifying the Object.prototype object here (big no-no)!

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

Yes, that's what Object.create does.

[Example 1 / 1] I would have expected Child.proto to be something naming Parent in the same way Parent.proto names Object.

It names Object only because that is a named function. There's no way to know the variable name Parent from the object reference at runtime.

[Example 1 / 2] We see that Child's prototype reference points to the members of Parent rather than the members of Parent.prototype. This is counterintuitive, to me at least, as I would have expected to see Parent.prototype's members b and g instead.

Yes, you're directly inheriting from the Parent object. .prototype is insignificant as stated above. Child.prototype === Object.prototype (it's inherited twice).

Example 2 - use a constructor instead. Adding stuff to its prototype member.

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

[Example 2 / 1] Here, Child's prototype references Parents.prototype's members. This is what I expected above?

No. The new operator creates a prototype reference to the value of the .prototype member of the supplied constructor function - Parent.prototype in this case.

// create differently
var Sibling = Object.create(Parent);

Again, here you're creating an object that has a prototype reference to a function. Not what you want - instead use

var sibling = Object.create(Parent.prototype);
Parent.call(sibling); // optionall apply the constructor on it, like `new` does

[Example 2 / 2] On the other hand, Sibling's prototype reference is now a function, which is the prototype reference of Parent.

To be more exact, it is the Parent function itself. That's why your Sibling does inherit a .prototype property.

Example 3 - This seems to be the way to preserve the parent object's prototype reference, but you lose its prototype member:

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

This is necessary to set up the inheritance (prototype reference) chain for the constructor way. It's the standard way to implement a "class" pattern in JavaScript.

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

As you see here, both Parent and Child are functions - constructor functions to be exact. You would need to invoke them:

var childInstance = new Child();
console.log(childInstance) // {c: "2"}
console.log(childInstance.prototype) // undefined - it's not a constructor
console.log(childInstance.__proto__) // {} - the Child.prototype object
console.log(childInstance.__proto__.__proto__) // the Parent.prototype object
console.log(childInstance.__proto__.__proto__.__proto__) // the Object.prototype object

However, you forgot one step. childInstance does inherit b and g from Parent.prototype, but does not have the a and f proeprties created in the Parent constructor. For correct inheritance in the constructor model you also have to apply the Parent constructor on each instance, preferably inside the Child constructor:

function Child() {
    Parent.call(this);
    this.c = "2";
}
var childInstance = new Child();
console.log(childInstance) // {a: 1, f: [Function …], c: "2"}
like image 92
Bergi Avatar answered Jan 23 '26 20:01

Bergi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!