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.
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. 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. It kind of started to sound like dilemma of hen and chicken.
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?
Also, according to Mozilla documentation every javascript function
is extension of Function
object but when you console.log(Function.prototype.constructor)
it is again a function. Now how can you use something to create it self (Mind = blown).
Last thing, Function.prototype
is a function but I can access the constructor
function using Function.prototype.constructor
does that mean Function.prototype
is a function which returns the prototype
object
4 Answers 4
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 bynew 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
isFunction.prototype
Object.prototype
is the object prototype object.- The prototype of
Object.prototype
isnull
- The prototype of
Function
isFunction.prototype
-- this is one of the rare situations whereFunction.prototype
is actually the prototype ofFunction
! Function.prototype
is the function prototype object.- The prototype of
Function.prototype
isObject.prototype
Let's suppose we make a function Foo.
- The prototype of
Foo
isFunction.prototype
. Foo.prototype
is the Foo prototype object.- The prototype of
Foo.prototype
isObject.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 aconstructor
property? No. Therefore we look at the prototype. - The prototype of
lizard
isReptile.prototype
, which isAnimal
. - Does
Animal
have aconstructor
property? No. So we look at it's prototype. - The prototype of
Animal
isObject.prototype
, andObject.prototype.constructor
is created by the runtime and equal toObject
. - 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 printconsole.log(Object.prototype)
it prints an object. Why isFunction.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.
-
9Finally, a short, concise, easy to understand explanation of JavaScript! Excellent! How could any of us possibly be confused? :) Although in all seriousness, the last bit about objects being lookup tables really is the key. There is a method to the madness --- but it is still madness...Greg Burghardt– Greg Burghardt2018年04月20日 16:31:15 +00:00Commented Apr 20, 2018 at 16:31
-
4@GregBurghardt: I agree it looks complex at first, but the complexity is the consequence of simple rules. Every object has a
__proto__
. The__proto__
of the object prototype is null. The__proto__
ofnew X()
isX.prototype
. All function objects have the function prototype for__proto__
except for the function prototype itself.Object
andFunction
and the function prototype are functions. Those rules are all straightforward, and they determine the topology of the graph of initial objects.Eric Lippert– Eric Lippert2018年04月20日 17:57:39 +00:00Commented Apr 20, 2018 at 17:57
You have many excellent answers already, but I just want to give a short and clear answer to your answer about how all of this works, and that answer is:
MAGIC!!!
Really, that's it.
The people who implement ECMAScript execution engines have to implement ECMAScript's rules, but not abide by them within their implementation.
The ECMAScript Specification says that A inherits from B but B is an instance of A? No problem! Create A first with a prototype pointer of NULL
, create B as an instance of A, then fix up the prototype pointer of A to point to B afterwards. Easy peasy.
You say, but wait, there is no way to change the prototype pointer in ECMAScript! But, here's the thing: this code isn't running on the ECMAScript engine, this code is the ECMAScript engine. It does have access to internals of the objects that ECMAScript code running on the engine doesn't have. In short: it can do whatever it wants.
By the way, if you really want to, you only have to do this once: afterwards, you can for example dump your internal memory and load this dump everytime you start up your ECMAScript engine.
Note that all of this still applies, even if the ECMAScript engine itself were written in ECMAScript (as is actually the case for Mozilla Narcissus, for example). Even then, the ECMAScript code that implements the engine still has full access to the engine it is implementing, although it of course doesn't have access to the engine it is running on.
From ECMA spec 1
ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather, supports constructors which create objects by executing code that allocates storage for the objects and initializes all or part of them by assigning initial values to their properties. All functions including constructors are objects, but not all objects are constructors.
I don't see how it could be any more clear!!! </sarcasm>
Further down we see:
Prototype A prototype is an object used to implement structure, state, and behavior inheritance in ECMAScript. When a constructor creates an object, that object implicitly references the constructor’s associated prototype for the purpose of resolving property references. The constructor’s associated prototype can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype.
So we can see that a prototype is an object, but not necessarily a function object.
Also, we have this interesting titbit
http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects
The Object constructor is the %Object% intrinsic object and the initial value of the Object property of the global object.
and
The Function constructor is the %Function% intrinsic object and the initial value of the Function property of the global object.
-
Now it does. ECMA6 allows you to create classes and instantiate objects from them.ncmathsadist– ncmathsadist2018年04月19日 13:37:22 +00:00Commented Apr 19, 2018 at 13:37
-
2@ncmathsadist ES6 classes are just a syntactic sugar, the semantics are the same.Hamza Fatmi– Hamza Fatmi2018年04月19日 13:49:15 +00:00Commented Apr 19, 2018 at 13:49
-
1Your
sarcasm
moniker otherwise, this text really is quite opaque to a beginner.Robert Harvey– Robert Harvey2018年04月19日 14:54:30 +00:00Commented Apr 19, 2018 at 14:54 -
true, ill add more later, need to do some diggingEwan– Ewan2018年04月19日 15:20:51 +00:00Commented Apr 19, 2018 at 15:20
-
1erm? to point out that it isn't clear from the docEwan– Ewan2018年04月19日 18:02:19 +00:00Commented Apr 19, 2018 at 18:02
The following types encompass every value in JavaScript:
boolean
number
undefined
(which includes the single valueundefined
)string
symbol
(abstract unique "things" that are compared by reference)object
Every object (i.e. everything) in JavaScript has a prototype, which is a kind of object.
The prototype contains functions, which are also a kind of object1.
Objects also have a constructor, which is a function, and therefore a kind of object.
It's all recursive, but the implementation is able to do it automagically because, unlike JavaScript code, it can create objects without having to call JavaScript functions (since objects are just memory that the implementation controls).
Most object systems in many dynamically typed languages are circular2 like this. For example, in Python, classes are objects, and the class of classes is type
, so type
is therefore an instance of itself.
The best idea is to just use the tools that the language provides, and not think too much about how they got there.
1 Functions are rather special because they are callable, and they are the only values that can contain opaque data (their body and possibly a closure).
2 It's actually more of a tortured, branching ribbon bent backwards over itself, but "circular" is close enough.
Explore related questions
See similar questions with these tags.
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.