O. Shekriladze
O. Shekriladze

Reputation: 1536

Javascript prototype chain behaves unexpectedly

Here is my code:

var Person = new Function() // same as function Person(){}
var john = new Person()

now I have prototype chain like that: Object -> Function -> Person -> john

Now I am doing something like that:

Function.prototype.bar = "boo"

So I expect to have Person.bar and john.bar to be "boo"

Person.bar  // boo
john.bar    // undefined

so what happened? I dived into and found out that john.__proto__ is prototype of Person, but john.__proto__.__proto__ is NOT prototype of Function, it's prototype of Object, so I lost one piece of chain(Finction). that's why john.bar was undefined. So why is this happening? shouldn't I be able to access Function prototype properties from john?

Upvotes: 2

Views: 71

Answers (2)

Aadit M Shah
Aadit M Shah

Reputation: 74204

Consider the following image taken from this answer.

As you can see, when you create a function in JavaScript a new prototype object is automatically created as well.

function Person() {}

Hence, the above code is actually:

function Person() {}
Person.prototype = { constructor: Person };

Now, you should also understand that the __proto__ property of a function is different from its prototype property.

function Person() {}

console.log(Person.__proto__ === Function.prototype); // true
console.log(Person.prototype !== Function.prototype); // true

When you create an instance of Person using the new keyword, that instance object inherits from Person.prototype and not from Person or Person.__proto__:

function Person() {}

const john = new Person;

console.log(john.__proto__ !== Person);           // true
console.log(john.__proto__ !== Person.__proto__); // true
console.log(john.__proto__ === Person.prototype); // true

The same goes for functions created by calling new Function.

const Person = new Function;

console.log(Person.__proto__ === Function.prototype); // true
console.log(Person.prototype !== Function.prototype); // true

That's the reason why john does not inherit from Function.prototype. The prototype chain for john is as follows.

      __proto__                     __proto__                     __proto__
john -----------> Person.prototype -----------> Object.prototype -----------> null

Whereas the prototype chain of Person is as follows:

        __proto__                       __proto__                     __proto__
Person -----------> Function.prototype -----------> Object.prototype -----------> null

Here's the proof:

const Person = new Function;
const john   = new Person;

console.log(john.__proto__                     === Person.prototype); // true
console.log(john.__proto__.__proto__           === Object.prototype); // true
console.log(john.__proto__.__proto__.__proto__ === null);             // true

console.log(Person.__proto__                     === Function.prototype); // true
console.log(Person.__proto__.__proto__           === Object.prototype);   // true
console.log(Person.__proto__.__proto__.__proto__ === null);               // true

This is because Person.prototype !== Person.__proto__.


Now, you may ask why do we have both the prototype and the __proto__ properties. The reason for this is because an object in JavaScript (let's call it derived) inherits from another object (let's call it base) if and only if derived.__proto__ = base. Consider the following code:

const base = { foo: 10 };

const derived = {};

derived.__proto__ = base; // derived now inherits from base

console.log(derived.foo); // 10

const newBase = { bar: 20 };

derived.__proto__ = newBase; // derived no longer inherits from base, it inherits from newBase

console.log(derived.foo); // undefined
console.log(derived.bar); // 20

Now, when you create an instance of a function the instance actually inherits from the prototype of the function. It doesn't inherit from the function.

function Person() {}

const john = new Person;

The above code is equivalent to:

function Person() {}
Person.prototype = { constructor: Person };

const john = { __proto__: Person.prototype };
Person.call(john);

Hope that clears things.

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370689

When you have

var Person = new Function()

you get an empty function which does nothing and returns nothing, eg

function Person() {
}

When you create an instance of Person with new, the newly created object will look at Person.prototype, and since Person.prototype inherits from Object (not Function), you won't see the .bar property.

function Person() {
}
const john = new Person();
console.log(
  Object.getPrototypeOf(john) === Person.prototype,
  Object.getPrototypeOf(Person.prototype) === Object.prototype
);

It's really weird to try to create an instance of a function, but if you wanted to, I suppose you could get what you're looking for by having Person return a Function instance:

Function.prototype.bar = "boo"
function Person() {
  return new Function();
};

const john = new Person();
console.log(john.bar);

I'd highly recommend not trying to create Function instances like this, though.

If you want an (odd) inheritance chain of

Object -> Function -> Person -> john

then you can use Object.create:

//const Person = Object.create(Function);
// or
const Person = new Function();

const john = Object.create(Person);
Function.prototype.foo = 'foo';
console.log(john.foo);

Upvotes: 4

Related Questions