16

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

  • since a function is an object, that mean that the 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 like Function.prototype and Object.prototype might just be interpreted in a special way by the engine. – Walfrat Apr 19 '18 at 13:12
  • 1
    Unless you're looking to implement a standards-compliant JavaScript compiler, you really don't need to worry about this stuff. If you're looking to get something useful done, you're off course. – Jared Smith Apr 19 '18 at 20:21
  • 5
    FYI the usual phrase in English for "the dilemma of hen and chicken" is "the chicken and egg problem", namely "which came first, the chicken or the egg?" (Of course the answer is the egg. Oviparous animals were around for millions of years before chickens.) – Eric Lippert Apr 19 '18 at 21:53
32

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".

enter image description here

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.

  • 9
    Finally, 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 Apr 20 '18 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__ of new X() is X.prototype. All function objects have the function prototype for __proto__ except for the function prototype itself. Object and Function and the function prototype are functions. Those rules are all straightforward, and they determine the topology of the graph of initial objects. – Eric Lippert Apr 20 '18 at 17:57
6

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.

3

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 Apr 19 '18 at 13:37
  • 2
    @ncmathsadist ES6 classes are just a syntactic sugar, the semantics are the same. – Hamza Fatmi Apr 19 '18 at 13:49
  • 1
    Your sarcasm moniker otherwise, this text really is quite opaque to a beginner. – Robert Harvey Apr 19 '18 at 14:54
  • true, ill add more later, need to do some digging – Ewan Apr 19 '18 at 15:20
  • 1
    erm? to point out that it isn't clear from the doc – Ewan Apr 19 '18 at 18:02
1

The following types encompass every value in JavaScript:

  • boolean
  • number
  • undefined (which includes the single value undefined)
  • 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.

nested

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.