I am trying to understand behind the curtain scenes of Javascript and kind of stuck in understanding the creation of built in objects, specially Object and Function and the relation between them.
It is complicated, it is easy to misunderstand, and a great many beginner Javascript books get it wrong, so do not trust everything you read.
I was one of the implementers of Microsoft's JS engine in the 1990s and on the standardization committee, and I made a number of mistakes putting together this answer. (Though since I have not worked on this for over 15 years I can perhaps be forgiven.) It is tricky stuff. But once you understand prototype inheritance, it all makes sense.
When I read that all the built in objects like Array, String etc are extension (inherited) from Object I assumed that Object is the first built in object that gets created and rest of the objects inherits from it.
Start by throwing away everything you know about class-based inheritance. JS uses prototype based inheritance.
Next, make sure you have a very clear definition in your head of what "inheritance" means. People used to OO languages like C# or Java or C++ think that inheritance means subtyping, but inheritance does not mean subtyping. Inheritance means that the members of one thing are also members of another thing. It does not necessarily mean that there is a subtyping relationship between those things! So many misunderstandings in type theory are the result of people not realizing that there is a difference.
But it doesn't make sense when you come to know that Objects can only be created by functions but then functions are also nothing but objects of Function.
This is simply false. Some objects are not created by calling new F
for some function F
. Some objects are created by the JS runtime out of nothing at all. There are eggs that were not laid by any chicken. They were just created by the runtime when it started up.
Let's say what the rules are and maybe that will help.
- Every object instance has a prototype object.
- In some cases that prototype can be
null
.
- If you access a member on an object instance, and the object does not have that member, then the object defers to its prototype, or stops if the prototype is null.
- The
prototype
member of an object is typically not the prototype of the object.
- Rather, the
prototype
member of a function object F is the object that will become the prototype of the object created by new F()
.
- In some implementations, instances get a
__proto__
member that really does give their prototype. (This is now deprecated. Don't rely on it.)
- Function objects get a brand-new default object assigned to
prototype
when they are created.
- The prototype of a function object is, of course
Function.prototype
.
Let's sum up.
- The prototype of
Object
is Function.prototype
Object.prototype
is the object prototype object.
- The prototype of
Object.prototype
is null
- The prototype of
Function
is Function.prototype
-- this is one of the rare situations where Function.prototype
is actually the prototype of Function
!
Function.prototype
is the function prototype object.
- The prototype of
Function.prototype
is Object.prototype
Let's suppose we make a function Foo.
- The prototype of
Foo
is Function.prototype
.
Foo.prototype
is the Foo prototype object.
- The prototype of
Foo.prototype
is Object.prototype
.
Let's suppose we say new Foo()
- The prototype of the new object is
Foo.prototype
Make sure that makes sense. Let's draw it. Ovals are object instances. Edges are either __proto__
meaning "the prototype of", or prototype
meaning "the prototype
property of".
All the runtime has to do is create all those objects and assign their various properties accordingly. I'm sure you can see how that would be done.
Now let's look at an example that tests your knowledge.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
What does this print?
Well, what does instanceof
mean? honda instanceof Car
means "is Car.prototype
equal to any object on honda
's prototype chain?"
Yes it is. honda
's prototype is Car.prototype
, so we're done. This prints true.
What about the second one?
honda.constructor
does not exist so we consult the prototype, which is Car.prototype
. When the Car.prototype
object was created it was automatically given a property constructor
equal to Car
, so this is true.
Now what about this?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
What does this program print?
Again, lizard instanceof Reptile
means "is Reptile.prototype
equal to any object on lizard
's prototype chain?"
Yes it is. lizard
's prototype is Reptile.prototype
, so we're done. This prints true.
Now, what about
print(lizard.constructor == Reptile);
You might think that this also prints true, since lizard
was constructed with new Reptile
but you would be wrong. Reason it out.
- Does
lizard
have a constructor
property? No. Therefore we look at the prototype.
- The prototype of
lizard
is Reptile.prototype
, which is Animal
.
- Does
Animal
have a constructor
property? No. So we look at it's prototype.
- The prototype of
Animal
is Object.prototype
, and Object.prototype.constructor
is created by the runtime and equal to Object
.
- So this prints false.
We should have said Reptile.prototype.constructor = Reptile;
at some point in there, but we did not remember to!
Make sure that all makes sense to you. Draw some boxes and arrows if it's still confusing.
The other extremely confusing thing is, if I console.log(Function.prototype)
it prints a function but when I print console.log(Object.prototype)
it prints an object. Why is Function.prototype
a function when it was meant to be an object?
The function prototype is defined as a function which, when called, returns undefined
. We already know that Function.prototype
is the Function
prototype, oddly enough. So therefore Function.prototype()
is legal, and when you do it, you get undefined
back. So it's a function.
The Object
prototype does not have this property; it is not callable. It's just an object.
when you console.log(Function.prototype.constructor)
it is again a function.
Function.prototype.constructor
is just Function
, obviously. And Function
is a function.
Now how can you use something to create it self (Mind = blown).
You are over-thinking this. All that is required is that the runtime creates a bunch of objects when it starts up. Objects are just lookup tables that associate strings with objects. When the runtime starts up, all it has to do is create a few dozen blank objects, and then start assigning the prototype
, __proto__
, constructor
, and so on properties of each object until they make the graph that they need to make.
It will be helpful if you take that diagram I gave you above and add constructor
edges to it. You'll quickly see that this is a very simple object graph and that the runtime will have no problem creating it.
A good exercise would be to do it yourself. Here, I'll start you off. We'll use my__proto__
to mean "the prototype object of" and myprototype
to mean "the prototype property of".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
And so on. Can you fill in the rest of the program to construct a set of objects that has the same topology as the "real" Javascript built-in objects? If you do so, you'll find it is extremely easy.
Objects in JavaScript are just lookup tables that associate strings with other objects. That's it! There's no magic here. You're getting yourself tied in knots because you are imagining constraints that do not actually exist, like that every object had to be created by a constructor.
Functions are just objects that have an additional capability: to be called. So go through your little simulation program and add an .mycallable
property to every object that indicates whether it is callable or not. It's as simple as that.
Function.prototype
can be a function and have inner fields. So no you don't execute the prototype function when going through it's structure. Finally remember that there is an engine interpreting Javascript, so Object and Function are probably created within the engine and not from Javascript and special reference likeFunction.prototype
andObject.prototype
might just be interpreted in a special way by the engine.