Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript __proto__ - clarification?

Tags:

javascript

I have this function :

function Foo(){}

According to this picture :

enter image description here

>> Foo.prototype   ->    Foo {}

enter image description here

So if I write :

Foo.prototype.constructor

Now - it references to the constructor function which created the Foo{} object which is function Foo(){}

enter image description here

All ok.

So where is the question ?

Foo.prototype is an object instance --> Foo {}

And who is the constructor function of Foo{} ?

it is the the function Foo(){}

you can see it here in yellow :

enter image description here

ok

But I was told here that :

enter image description here

So Foo.prototype.__proto__ should reference to the prototype of function Foo(){} and not to Object.prototype !

If I translate it :

    __proto__               is the                   ctor's                        prototype
    _________________________________________________________________________________________
  Foo.prototype.__proto__     is the               function Foo(){}               Foo.prototype

but this is wrong

And i'm probably wrong because :

enter image description here

What am I missing ?

like image 318
Royi Namir Avatar asked Oct 02 '22 13:10

Royi Namir


1 Answers

Note: First off, __proto__ is non-standard, use Object.getPrototypeOf, though for shortness, and because I'm lazy, I did use prototype quite a few times in this answer

Well, you're thrown of by your initial assumption, if I'm reading you correctly:

Foo.prototype -> Foo{}

The prototype of Foo (which is a constructor) is, by definition an instance of Foo. But, because a constructor is a function, and a constructor returns an object of a particular instance, the prototype will be an augmented Object either way.
I'm not a big fan of comparing prototypal ineritance to classical OOP, but think of it like this:

The Foo prototype is sort of a mutable class definition (you can add methods and properties as you go along), the constructor creates an object that augments this prototype, and adds another layer of properties/methods on an instance level.
Hence Foo.prototype instanceof Foo is true. But, Foo being an object:

Object.getPrototypeOf(Foo.prototype) === Object.prototype

is true. Just as

Object.getPrototypeOf(Array.prototype) === Object.prototype.

Translate this to JS (prototype)-speak, and you get something like:

For each function object JS creates, this object is assigned a prototype property. The prototype property is an instance of Object, but it has 1 special property.
If one attempts to access a property or method on an object, and JS cannot find this property defined at the instance level, JS will attempt to resolve the name on the prototype property:

instance.nonExistantProperty;
//js scans instance variable, doesn't find property:
instance.prototype.nonExistantProperty
//js uses prototype, if not found:
instance.prototype.prototype.nonExistantProperty
//all the way up to Object.prototype, which does not have a prototype, so here an exception is raised (TypeError is thrown)

Here's a short schematic representation of this lookup, which I copied from one of my older posts here, it might be worth a look, also check the linked answers at the bottom, they go into even more details on this matter

[      F.divide      ]<=========================================================\ \
F[divide] ===> JS checks instance for property divide                           | |
 /\ ||                                                                          | |
 || || --> property found @instance, return value-------------------------------| |
 || ||                                                                          | |
 || ===========> Function.prototype.divide could not be found, check prototype  | |
 ||      ||                                                                     | |
 ||      ||--> property found @Function.prototype, return-----------------------| |
 ||      ||                                                                     | |
 ||      ==========> Object.prototype.divide: not found check prototype?        | |
 ||          ||                                                                 | |
 ||          ||--> property found @Object.prototype, return---------------------|_|
 ||          ||                                                                 |=|
 ||          =======>prototype is null, return "undefined.divide"~~~~~~~~~~~~~~~|X|
 ||                                                                             \ /
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property 'x' of undefined

That's basically it.

Now, perhaps you're also a tad thwarted as to why Foo.prototype.constructor references the Foo function.
Again, this is quite easy, because each instance is expected to contain all information you might require to determine which type of object you're dealing with:

function Foo{}
var inst = new Foo();
console.log(inst.constructor);//references the constructor function

Remember, all instances returned by Foo return an object, that references all of the prototype properties, and (optionally) some properties at instance level, too.

Why, then, would you even bother creating such instances? Again this is perfectly simple: changing an instance, does not change the prototype:

console.log(Foo.prototype.constructor === inst.constructor);//true
inst.constructor = function(){};//override constructor property
console.log(Foo.prototype.constructor === inst.constructor);//false, prototype did not change
like image 174
Elias Van Ootegem Avatar answered Oct 07 '22 18:10

Elias Van Ootegem