http://halicery.com/Programming/ECMANOTES.html

ECMAScript notes - JavaScript Objects and Functions

© 2016 Attila Tarpai (tarpai76 ... gmail)

I'm always interested in how a programming language could be implemented, how structures might sit in memory and work under the surface. JavaScript is an interpreted language, it has a very simple and general model to store code- and data built up from the source code. This is my first impression after reading the ECMA Standard[1] and trying a few things in Chrome.

No warranties for its correctness...

Objects

In its purest form, everything is an object in JavaScript: things with named properties. Every object has also a hidden[2], implicit reference to another object, called the prototype. Named properties are hanging on the object, can dynamically added or removed. A named property can be anything from storing simple numbers to references to other objects, including functions:

   implicit              
   prototype              
       |               
+------|------+           
|             |-- "prop1" 
|             |-- "prop2" 
|   object    |-- "prop3" 
|             |           
|             |           
+-------------+           

These simple building rules give possibilities to implement many constructs, deep object-trees, combined data- and function reference hierarchies.

Function objects

Function definitions in JavaScript end up as special objects containing the function body, but they are also just objects with named properties and implicit prototype. The difference is that they're callable, using the call-operator executes the function body.

   implicit             
   prototype             
       |                 
+------|------+          
|             |-- "prop1"
|  function   |-- "prop2"
|   object    |          
|             |          
|  { body }   |          
+-------------+          

Function definition

Every function defined in script is also a constructor function. This is per Standard[3].

     null                                       
      |                                               null        
   Object.prototype                                    |          
      |                                           Object.prototype
   Function.prototype                                  |          
      |                                            +---|----+     
+-----|----------+                                 |        |
|                |- "prototype" -----------------> |        |     
|                |                                 |        |     
|                | <---------------- "constructor"-|        |     
|                |                                 +--------+     
|                |                                 F.prototype    
|    { body }    |                                
|                |
+----------------+                
        F 

After function definition, f.ex. function F(){} two objects get created: the function object (F) and another, empty object (F.prototype) used in constructor calls, linked together through named properties. Function objects store only one function body and the difference will be in usage: via function-call or constructor-call.

Function-call and this

Only objects that has [[Call]] internally are callable. A normal function definition makes such an object, places {body} into [[Call]], sets implicit prototype to Function.prototype and the new object will be callable.

A general, callable object's function could look like this in C-like:

   Function.prototype
           |         
+----------|----------+
|                     |
|                     |
| [[Call]](this, ...) |
| {                   |
|   body              |
| }                   |
+---------------------+
           F

There is always a hidden this parameter passed to every function calls. For example:

F() enters function body with: F->[[Call]](this)
F(10) enters function body with: F->[[Call]](this, 10)

There is a tremendous amount of information out there about JavaScript and this...

Constructor-call

When a function is called with the new keyword, it's a constructor-call. Internally, this could look like this in C:

                        
   Function.prototype   
           |            
+----------|-----------+
|                      |
|                      |- "prototype" -------->
| [[Call]](this, ...)  |
| {                    |
|   body               |
| }                    |
|                      |
|                      |
| [[Construct]](...)   |
| {                    |
|   obj= new Object()  |
|   [[Call]](obj, ...) |
|   return obj         |
| }                    |
+----------------------+
           F


[[Construct]] is called instead, an internal, wrapper function for [[Call]], with optional parameters. It creates a new object first, sets implicit prototype to "prototype", then passes the new object to [[Call]] as this along with the optional parameters. It then returns the new object (if [[Call]] didn't returned an object).

Note that there is no this parameter passed to [[Construct]], it will make for [[Call]].

Constructor-calls, such as new F(), are meant to return a new, initialized object:

     null                                        
      |                                                null          
   Object.prototype                                     |                  
      |                                            Object.prototype
   Function.prototype                                   |            
      |                                             +---|----+     
+-----|-----------+                                 |        |-"var1"
|                 |- "prototype" -----------------> |        |-"var2"     
|                 |                                 |        |     
|                 | <---------------- "constructor"-|        |     
|                 |                                 +--------+     
|                 |                                 F.prototype    
|    { body }     |                                     |
|                 |                                     |
+-----------------+                                     |
        F                                               |
                                                        |
                                        +---------+---------+---------+
                                        |         |         |         |    
                                        |         |         |         |    
                                    +---|---+ +---|---+ +---|---+ +---|---+
                                    |       | |       | |       | |       |
                                    |  obj  | |  obj  | |  obj  | |  obj  | 
                                    |       | |       | |       | |       |
                                    +-------+ +-------+ +-------+ +-------+
                                     new F()   new F()   new F()   new F() 

Calling F as constructor, new F():

Objects created by new F() all have implicit prototype set to F.prototype. The new objects share the F.prototype object and all its named properties through the prototype chain ("constructor", "var1" and "var2"). This is different from inheritance used in C++.

It is up to function body to use this mechanism or not: do something with the new object, add properties, initialize, or just execute some code and return something else.

The 4 main built-in

                                    null 
                                     |
                                     |
                             +-------|--------+
+----------<-- "constructor"-|                |
|                            |                |
|    +-------------------->  |                |
|    |                       |                |
|    |                       |    Object.     |
|    |                       |   prototype    |
|    |                       +----------------+
|    |                               |
|    |                               |
|    |                               |
|    |                       +-------|--------+
|    |                       |                |
|    |                       |                |-"constructor"--->----------+
|    |                       |                |                            |
|    |                       |                |                            |
|    |                       |   Function.    |<---------------------+     |
|    |                       |   prototype    |                      |     |
|    |                       +----------------+                      |     |
|    |                               |                               |     |
|    |                               |                               |     |
|    |                               |                               |     |
|    |                 +-------------+------------+                  |     |
|    |                 |                          |                  |     |
|    |                 |                          |                  |     |
|    |         +-------|--------+         +-------|--------+         |     |
|  "prototype"-|                |         |                |-"prototype"   |
|              |                |         |                |               |
|              |                |         |                |               |
|              |                |         |                |               |
+------------> |                |         |                |<--------------+
               |    Object()    |         |   Function()   |
               +----------------+         +----------------+

Object()

Object() is a built-in (constructor) function. Calling Object() or new Object() gives the same result per Standard: an empty ECMAScript object with implicit prototype set to Object.prototype.

Language short-cut: object literal { }. Using { name:value, … } will also add named properties with intialized values.

Function()

Function() is a built-in (constructor) function: F=[new] Function (p1, p2, …, pn, body) creates a user-defined function object and sets implicit prototype to Function.prototype. It also creates a new object for the function, the function prototype object, and transforms F into a constructor function. Function() and new Function() should give the same result per Standard.

The Function.prototype object

It is the parent object for all other function objects:

        |    Object.     |
        |   prototype    |            
        +----------------+            
                |                     
                |                     
        +-------|--------+            
        |                |-"apply"            
        | [[Call]](this) |-"call"
        | {              |-"bind"                       
        |   return       |                       
        |     undefined  |            
        |  }             |                            
        +----------------+                            
        Function.prototype
                |
                |
                |
                |
                +------------------------+------------------------+----- - - 
                |                        |                        |           
     +----------|----------+  +----------|----------+  +----------|----------+
     |                     |  |                     |  |                     |
     |                     |  |                     |  |                     |
     | [[Call]](this, ...) |  | [[Call]](this, ...) |  | [[Call]](this, ...) |
     | {                   |  | {                   |  | {                   |
     |   body              |  |   body              |  |   body              |
     | }                   |  | }                   |  | }                   |
     +---------------------+  +---------------------+  +---------------------+
               F1()                    F2()                     F3()           

He's just a very special fellow. Strangely, Function.prototype itself is a callable object, but with implicit prototype set to Object.prototype. There is a little piece of code there, calling Function.prototype() doesn't care about arguments and returns undefined (the purpose of this is a little unclear at the moment).

There are 2 interesting methods there (named properties that are function objects):

Function.prototype.call() method

There is an explicit mechanism to enter functions' [[Call]]: the Function.prototype.call() method. The call method is a function property of Function.prototype, accessible for every function objects through the prototype chain. Every function defined in the program has implicit prototype set to Function.prototype.

Now this is confusing. Function.prototype itself is a callable object. Function.prototype.call itself is a function object with implicit prototype set back to Function.prototype. Calling "call" from F will call back F with the provided parameters, passing thisArg as this with the other optional parameters.

I tried to understand the structure behind this madness and came up with this figure:

                                        Function.prototype
                                               |
                                       +-------|--------------------------+
         Object.prototype              |                                  |
                |                      | [[Call]](this, thisArg, ...)     |
        +-------|--------+             | {                                |
        |                |             |   return                         |
        | [[Call]](this) |-"call"--->  |     this->[[Call]](thisArg, ...) |
        | {              |             |  }                               |
        |   return       |             +----------------------------------+
        |     undefined  |                  Function.prototype.call        
        |  }             |                            
        +----------------+                            
        Function.prototype
                |
                |
                |         
     +----------|----------+
     |                     |
     |                     |
     | [[Call]](this, ...) |
     | {                   |
     |   body              |
     | }                   |
     +---------------------+
                F

Here I use Chrome, which lets me use call() without parameters. In ES5, thisArg is not optional. See the note on p. 119, Standard ECMA-262 5.1 Edition / June 2011[4].

>function f() { return this }
>undefined
>f()
>Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, ...
>f.call()
>Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, ...
>x=10
>10
>f.call(x)
>Number {[[PrimitiveValue]]: 10}

The Function.prototype.call() method is good f.ex. to override the default, hidden this passed to a function.

Calling Function.prototype.call()

The Function.prototype object itself is callable, calling it will enter the native code in Function.prototype:

The function does nothing and returns undefined. The purpose? No clue.

A detour: Function.prototype.call.call()???

Consider this fig:

        |    Object.     |
        |   prototype    |             
        +----------------+                      
                |
                |
                |                 Function.prototype
                |      +-----------------------+
                |      |                       |
                |      |      !        +-------|--------------------------+
                |      |               |                                  |
                |      |               | [[Call]](this, thisArg, ...)     |
        +-------|--------+             | {                                |
        |                |             |   return                         |
        | [[Call]](this) |-"call"--->  |     this->[[Call]](thisArg, ...) |
        | {              |             | }                                |
        |   return       |             +----------------------------------+
        |     undefined  |                  Function.prototype.call          
        | }              |                            
        +----------------+                            
        Function.prototype
                |
                |
                |         
     +----------|----------+
     |                     |
     |                     |
     | [[Call]](this, ...) |
     | {                   |
     |   body              |
     | }                   |
     +---------------------+
                F

Here is a JavaScript language glitch.. syntactically it should be correct, but would spin execution forever until memory is eaten up.. call() calls "call"-s [[Call]], which would call "call"-s [[Call]] and so on. Simple....

Lets try these in Chrome:

>Function.prototype.call.call     
>function call() { [native code] }

OK, it's a function, so lets call it:

>Function.prototype.call.call()
>VM76:1 Uncaught TypeError: Function.prototype.call.call is not a function
     at <anonymous>:1:25


Hm.. as far as I know, call is a function. And we talk about the same function:

>typeof Function.prototype.call.call
>"function"
>
>Function.prototype.call.call === Function.prototype.call
>true

I think Chrome pushes the breaks here and says not a function to avoid Function.prototype.call.call() end up in a circular, infinite call. Or??

Function.prototype.bind()

There are hundreds of examples how bind() can be useful. This is how the mechanism works.

Function.prototype.bind itself is similar to "call", a named function property (method) of the Function.prototype object. The "bind" method is accessible for every function objects through the prototype chain (like for F here). A C-like pseudo code might look like this (note that thisArg is not optional in ES5):

                                        Function.prototype
                                               |
                                       +-------|-------------------------+
         Object.prototype              |                                 |
                |                      | [[Call]](this, thisArg, ...)    |
        +-------|--------+             | {                               |
        |                |             |     f= new-bind-function-object |
        | [[Call]](this) |-"bind"--->  |     f.TargetFunction= this      |
        | {              |             |     f.BoundThis= thisArg        |
        |   return       |             |     f.BoundArgs= ...            |
        |     undefined  |             |     return f                    |
        |  }             |             | }                               |
        +----------------+             +---------------------------------+  
        Function.prototype                   Function.prototype.bind
                |
                |
                |         
     +----------|----------+
     |                     |
     |                     |
     | [[Call]](this, ...) |
     | {                   |
     |   body              |
     | }                   |
     +---------------------+
                F

Creating the bind-function object

Calling bind(thisArg, ...) creates a special function object, which records all parameters of the call internally, including this. The new bind-function object lacks the "prototype" named property and has native code:

    Function.prototype          
           |                      
+----------|------------+
|                       |
|  TargetFunction       |
|  BoundThis            |  
|  BoundArgs            |
|                       |
|                       |
|       native          |
|        code           |
|                       |
+-----------------------+
   bind-function object

Generally,

These 3 are internal properties of the bind-function object and not accessible as named properties.

F.ex. writing Fb= F.bind(obj, 10, 20) will enter as Function.prototype.bind->[[Call]](F, obj, 10, 20) and returns this callable object:

    Function.prototype   
           |              
+----------|------------+
|                       |
|  TargetFunction = F   |
|  BoundThis = obj      |
|  BoundArgs = [10,20]  |
|                       |
|                       |
|       native          |
|        code           |
|                       |
+-----------------------+
          Fb

Using the bind-function object

The new bind-function object has native code for [[Call]]. It also has [[Construct]] and accepts constructor-calls via new.

An attempt in C-like:

   Function.prototype   
           |            
+----------|-------------------------------------------------+
|                                                            |
| TargetFunction                                             |
| BoundThis                                                  |  
| BoundArgs                                                  |
|                                                            |
|                                                            |
| [[Call]](this, ExtraArgs)                                  |
| {                                                          |
|  return                                                    |
|   TargetFunction->[[Call]](BoundThis, BoundArgs+ExtraArgs) |
| }                                                          |
|                                                            |
| [[Construct]](ExtraArgs)                                   |
| {                                                          |
|  return                                                    |
|   TargetFunction->[[Construct]](BoundArgs+ExtraArgs)       |
| }                                                          |
+------------------------------------------------------------+
                   bind-function object

Calling Fb accepts additional parameters, ExtraArgs. Fb discards this of the original caller and passes BoundThis instead to TargetFunction, supplies the stored parameters first (if any), then the additional parameters from the caller (if any):

                      this,                    BoundThis,           +--------+
                      ExtraArgs    +--------+  BoundArgs+ExtraArgs  |        |
    Fb(ExtraArgs)   -------------> |[[Call]]| --------------------> |[[Call]]|
                    <------------  |        | <-------------------  |        |
                        ret        +--------+     ret               |        |
                                      Fb()                          +--------+
                                                                        F()   

Calling Fb with new as constructor also accepts additional parameters, ExtraArgs. It simply calls the bound function's [[Construct]], concatenating the stored BoundArgs (if any) with ExtraArgs (if any):

                                                                    +--------+
                      ExtraArgs    +--------+  BoundArgs+ExtraArgs  |        |
new Fb(ExtraArgs)   -------------> | [[Cons | --------------------> | [[Cons |
                    <------------  | truct]]| <-------------------  | truct]]|
                        ret        +--------+     ret               |        |
                                      Fb()                          +--------+
                                                                        F()   

Both returns whatever the bound function returns.

That's it, the essence of it.

Global Object

Objects don't just hang in the air. Every object is referenced from one or more other objects through the implicit prototype chain or through names properties. The environment, where the interpreted code runs is also a JavaScript object, the host-specific Global Object. It has built-in named properties for language features, including the Object() and the Function() function objects to define code and data. New variables are attached to the Global Object by default. Keywords, like var or function, operators, like {} or () usually just end up calling functions of some built-in object.

 -----------
            |   ----------
 Global     |--|  var a   |
            |   ----------
            |
            |   -------------- 
            |--| function f() |
            |   --------------
            |
            |
            |              
            |  
            |  
            |   ---------- 
            |  |          |
            |--| Object() |
            |  |          |
            |   ---------- 
            |              
            |              
            |   
            |   
            |      
            |   ---------- 
            |  |          |
            |--|Function()|
            |  |          |
            |   ---------- 
            |              
            |-- "undefined" = undefined
            |-- "eval" = eval() function 
            |--
 -----------

Some commands in Chrome to verify the figs above

For the 4 main built-in and object and functions

Object itself is a function with name Object, implicit prototype is a function, no name:

> Object
> function Object() { [native code] }
>
>Object.__proto__
>function () {}  

This must be the Function.prototype:

>Object.__proto__ === Function.__proto__ 
>true
>
>Object.__proto__ === Function.prototype
>true

Object()'s "prototype" is an object, its "constructor" is the Object() function:

>Object === Object.prototype.constructor
>true

Function() is a function, both implicit- and explicit prototype is the same object:

>Function.__proto__ === Function.prototype
>true                                     

Function.prototype's implicit prototype is the Object.prototype:

>Object.prototype === Function.prototype.__proto__
>true

Function definition, which doesn't return anything and has no body:

>function F() { }
>undefined

Calling F() returns undefined:

>F()
>undefined

Calling F() as a constructor works and returns an empty object:

>new F()
> F {}
>   F__proto__: Object

Lets construct two objects with F():

>o1= new F(), o2= new F()

Add a property to P, the prototype-object:

>F.prototype.w=100
>100

Check whether o1 and o2 inherited this property:

>o1.w
>100
>
>o2.w
>100

Change this shared property through o1.. and see whether it has been changed through o2 (this will not work!):

>o1.w=4
>4
>
>o2.w
>100

We only added a new, named property 'w' to o1 too:

             +--------+           
             |        |- "w"
             |   P    |
             +--------+           
                 |                
                 |                
          +-----------+
          |           |    
          |           |    
     +--------+      +--------+
     |        |-"w"  |        |
     |  o1    |      |   o2   |
     +--------+      +--------+

The proper way to change a shared property is through the constructor function:

>F.prototype.w=5
>5
>o1.w
>4
>o2.w
>5

o1.w is it's own property with the same name and hit before going further in the prototype chain. o2.w retrieves 'w' from the prototype object.

F(): function call and what is this

My rule is simple at the moment: what's before F will be this. The call-operator will call F's [[Call]] passing either Global or an object. If missing, it's the Global object:

              F()      this <- Global
____.____.obj.F()      this <- obj

Lets try this in Chrome: make a function object that returns this. Reference and call the same function from 2 objects:

   +--------+
   |   o1   |-"f1" -----+
   +--------+           |       +-----------------------------+
                        +---->  |  function (){return this}   |
   +--------+           |       +-----------------------------+
   |   o2   |-"f2" -----+
   +--------+

Make object 1:

>o1= Object()
>Object {}

Add (and define) a function property:

>o1.f1 = function (){return this}
>function (){return this}

Make object 2:

>o2= Object()
>Object {}

Add the same function property:

>o2.f2 = o1.f1
>function (){return this}

Check if copy or the same (looks same to me)

>o1.f1 === o2.f2
>true

Doublecheck if copy or the same object by adding a property to the function object through o1.. and reading it through o2 (it's really the same object):

>o1.f1.a=100
>100
>o2.f2.a
>100

Now call the function through o1, then o2 and check what is this:

>o1.f1()
>Object {}
>o1.f1() === o1
>true
>o2.f2()
>Object {}
>o2.f2() === o2
>true

So.. it works according to the fig above, passing either o1 or o2 to f() as this.

Notes:

  1. ^ Standard ECMA-262 5.1 Edition / June 2011
  2. ^ Chrome exposes implicit reference in __proto__
  3. ^ See in 13.2: A prototype property is automatically created for every function, to allow for the possibility that the function will be used as a constructor.
  4. ^ NOTE The thisArg value is passed without modification as the this value. This is a change from Edition 3, where a undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value.

Fri Jan 13 20:36:20 UTC+0100 2017 © Attila Tarpai