in ECMAScript

ECMA-262-3 in detail. Chapter 2. Variable object.

Read this article in: Russian, Chinese, French.

Introduction

Always in programs we declare functions and variables which then successfully use building our systems. But how and where the interpreter finds our data (functions, variable)? What occurs, when we reference to needed objects?

Many ECMAScript programmers know that variables are closely related with the execution context:

var a = 10; // variable of the global context

(function () {
  var b = 20; // local variable of the function context
})();
 
alert(a); // 10
alert(b); // "b" is not defined
 

Also, many programmers know that the isolated scope in the current version of specification is created only by execution contexts with “function” code type. I.e., in contrast with C/C++, for example the block of for loop in ECMAScript does not create a local context:

for (var k in {a: 1, b: 2}) {
  alert(k);
}
 
alert(k); // variable "k" still in scope even the loop is finished

Let’s see in more details what occurs when we declare our data.

Data declaration

If variables are related with the execution context, it should know where its data are stored and how to get them. This mechanism is called a variable object.

A variable object (in abbreviated form — VO) is a special object related with an execution context and which stores:

  • variables (var, VariableDeclaration);
  • function declarations (FunctionDeclaration, in abbreviated form FD);
  • and function formal parameters

declared in the context.

Notice, in ES5 the concept of variable object is replaced with lexical environments model, which detailed description can be found in the appropriate chapter.

Schematically and for examples, it is possible to present variable object as a normal ECMAScript object:

VO = {};

And as we said, VO is a property of an execution context:

activeExecutionContext = {
  VO: {
    // context data (var, FD, function arguments)
  }
};

Indirect referencing to variables (via property names of VO) allows only variable object of the global context (where the global object is itself the variable object). For other contexts directly to reference the VO is not possible, it is purely mechanism of implementation.

When we declare a variable or a function, there is nothing else as creation of the new property of the VO with the name and value of our variable.

Example:

var a = 10;

function test(x) {
  var b = 20;
};

test(30);

And corresponding variable objects are:

// Variable object of the global context
VO(globalContext) = {
  a: 10,
  test: <reference to function>
};
 
// Variable object of the "test" function context
VO(test functionContext) = {
  x: 30,
  b: 20
};

But at implementation level (and specification) the variable object is an abstract essence. Physically, in concrete execution contexts, VO is named differently and has different initial structure.

Variable object in different execution contexts

Some operations (e.g. variable instantiation) and behavior of the variable object are common for all execution context types. From this viewpoint it is convenient to present the variable object as an abstract base thing. Function context can also define additional details related with the variable object.

AbstractVO (generic behavior of the variable instantiation process)

  ║
  ╠══> GlobalContextVO
  ║        (VO === this === global)
  ║
  ╚══> FunctionContextVO
           (VO === AO, <arguments> object and <formal parameters> are added)

Let’s consider it in detail.

Variable object in global context

Here, first it is necessary to give definition of the Global object.

Global object is the object which is created before entering any execution context; this object exists in the single copy, its properties are accessible from any place of the program, the life cycle of the global object ends with program end.

At creation the global object is initialized with such properties as Math, String, Date, parseInt etc., and also by additional objects among which can be the reference to the global object itself — for example, in BOM, window property of the global object refers to global object (however, not in all implementations):

global = {
  Math: <...>,
  String: <...>
  ...
  ...
  window: global
};
 

When referencing to properties of global object the prefix is usually omitted, because global object is not accessible directly by name. However, to get access to it is possible via this value in the global context, and also through recursive references to itself, for example window in BOM, therefore write simply:

String(10); // means global.String(10);

// with prefixes
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;

So, coming back to variable object of the global context — here variable object is the global object itself:

VO(globalContext) === global;

It is necessary to understand accurately this fact since for this reason declaring a variable in the global context, we have ability to reference it indirectly via property of the global object (for example when the variable name is unknown in advance):

var a = new String('test');

alert(a); // directly, is found in VO(globalContext): "test"

alert(window['a']); // indirectly via global === VO(globalContext): "test"
alert(a === this.a); // true
 
var aKey = 'a';
alert(window[aKey]); // indirectly, with dynamic property name: "test"
 

Variable object in function context

Regarding the execution context of functions — there VO is inaccessible directly, and its role plays so-called an activation object (in abbreviated form — AO).

VO(functionContext) === AO;

An activation object is created on entering the context of a function and initialized by property arguments which value is the Arguments object:

AO = {
  arguments: <ArgO>
};
 

Arguments object is a property of the activation object. It contains the following properties:

  • callee — the reference to the current function;
  • length — quantity of real passed arguments;
  • properties-indexes (integer, converted to string) which values are the values of function’s arguments (from left to right in the list of arguments). Quantity of these properties-indexes == arguments.length. Values of properties-indexes of the arguments object and present (really passed) formal parameters are shared.

Example:

function foo(x, y, z) {
 
  // quantity of defined function arguments (x, y, z)
  alert(foo.length); // 3

  // quantity of really passed arguments (only x, y)
  alert(arguments.length); // 2

  // reference of a function to itself
  alert(arguments.callee === foo); // true
 
  // parameters sharing

  alert(x === arguments[0]); // true
  alert(x); // 10
 
  arguments[0] = 20;
  alert(x); // 20
 
  x = 30;
  alert(arguments[0]); // 30
 
  // however, for not passed argument z,
  // related index-property of the arguments
  // object is not shared
 
  z = 40;
  alert(arguments[2]); // undefined
 
  arguments[2] = 50;
  alert(z); // 40
 
}
 
foo(10, 20);
 

Concerning the last case, in older versions of Google Chrome there was a bug — there parameter z and arguments[2] were also shared.

In ES5 the concept of activation object is also replaced with common and single model of lexical environments.

Phases of processing the context code

Now we have reached the main point of this article. Processing of the execution context code is divided on two basic stages:

  1. Entering the execution context;
  2. Code execution.

Modifications of the variable object are closely related with these two phases.

Notice, that processing of these two stages are the general behavior and independent from the type of the context (i.e. it is fair for both: global and function contexts).

Entering the execution context

On entering the execution context (but before the code execution), VO is filled with the following properties (they have already been described at the beginning):

  • for each formal parameter of a function (if we are in function execution context)
  • — a property of the variable object with a name and value of formal parameter is created; for not passed parameters — property of VO with a name of formal parameter and value undefined is created;

  • for each function declaration (FunctionDeclaration, FD)
  • — a property of the variable object with a name and value of a function-object is created; if the variable object already contains a property with the same name, replace its value and attributes;

  • for each variable declaration (var, VariableDeclaration)
  • — a property of the variable object with a variable name and value undefined is created; if the variable name is the same as a name of already declared formal parameter or a function, the variable declaration does not disturb the existing property.

Let’s see on the example:

function test(a, b) {
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}
 
test(10); // call
 

On entering the test function context with the passed parameter 10, AO is the following:

AO(test) = {
  a: 10,
  b: undefined,
  c: undefined,
  d: <reference to FunctionDeclaration "d">
  e: undefined
};

Notice, that AO does not contain function x. This is because x is not a function declaration but the function-expression (FunctionExpression, in abbreviated form FE) which does not affect on VO.

However, function _e is also a function-expression, but as we will see below, because of assigning it to the variable e, it becomes accessible via the e name. The difference of a FunctionDeclaration from the FunctionExpression is in detail discussed in Chapter 5. Functions.

And after that there comes the second phase of processing of a context code — the code execution stage.

Code execution

By this moment, AO/VO is already filled by properties (though, not all of them have the real values passed by us, most of them yet still have initial value undefined).

Considering all the same example, AO/VO during the code interpretation is modified as follows:

AO['c'] = 10;
AO['e'] = <reference to FunctionExpression "_e">;
 

Once again I notice that function expression _e is still in memory only because it is saved to declared variable e. But the function expression x is not in the AO/VO. If we try to call x function before or even after definition, we get an error: "x" is not defined. Unsaved to a variable function expression can be called only with its definition (in place) or recursively.

One more (classical) example:

alert(x); // function

var x = 10;
alert(x); // 10

x = 20;

function x() {}

alert(x); // 20
 

Why in the first alert x is a function and moreover is accessible before the declaration? Why it’s not 10 or 20? Because, according to the rule — VO is filled with function declarations on entering the context. Also, at the same phase, on entering the context, there is a variable declaration x, but as we mentioned above, the step of variable declarations semantically goes after function and formal parameters declarations and on this phase do not disturb the value of the already declared function or formal parameter with the same name. Therefore, on entering the context VO is filled as follows:

VO = {};
 
VO['x'] = <reference to FunctionDeclaration "x">
 
// found var x = 10;
// if function "x" would not be already defined 
// then "x" be undefined, but in our case
// variable declaration does not disturb
// the value of the function with the same name
 
VO['x'] = <the value is not disturbed, still function>
 

And then at code execution phase, VO is modified as follows:

VO['x'] = 10;
VO['x'] = 20;
 

what we can see in the second and third alerts.

In the example below we see again that variables are put into the VO on entering the context phase (so, the else block is never executed, but nevertheless, the variable b exists in VO):

if (true) {
  var a = 1;
} else {
  var b = 2;
}

alert(a); // 1
alert(b); // undefined, but not "b is not defined"
 

About variables

Often various articles and even books on JavaScript claim that: “it is possible to declare global variables using var keyword (in the global context) and without using var keyword (in any place)”. It is not so. Remember:

variables are declared only with using var keyword.

And assignments like:

 a = 10; 

just create the new property (but not the variable) of the global object. “Not the variable” is not in the sense that it cannot be changed, but “not the variable” in concept of variables in ECMAScript (which then also become properties of the global object because of VO(globalContext) === global, we remember, yeah?).

And the difference is the following (let’s show on the example):

alert(a); // undefined
alert(b); // "b" is not defined

b = 10;
var a = 20;
 

All again depends on VO and phases of its modifications (entering the context stage and the code execution stage):

Entering the context:

VO = {
  a: undefined
};
 

We see that at this phase there is no any b since it is not a variable, b will appear only at code execution phase (but in our case won’t since there is an error).

Let’s change the code:

alert(a); // undefined, we know why

b = 10;
alert(b); // 10, created at code execution

var a = 20;
alert(a); // 20, modified at code execution
 

There is one more important point concerning variables. Variables, in contrast with simple properties, have attribute {DontDelete}, meaning impossibility to remove a variable via the delete operator:

a = 10;
alert(window.a); // 10

alert(delete a); // true

alert(window.a); // undefined

var b = 20;
alert(window.b); // 20

alert(delete b); // false

alert(window.b); // still 20
 

Note, in ES5 {DontDelete} is renamed into the [[Configurable]] and can be manually managed via Object.defineProperty method.

However there is one execution context on which this rule does not affect. It is the eval context: there {DontDelete} attribute is not set for variables:

eval('var a = 10;');
alert(window.a); // 10

alert(delete a); // true

alert(window.a); // undefined

For those who test these examples in console of some debug tool, e.g. Firebug: notice, that Firebug also uses eval to execute your code from the console. So there vars also do not have {DontDelete} and can be deleted.

Feature of implementations: property __parent__

As it was already noted, by the standard, to get direct access to the activation object is impossible. However, in some implementations, namely in SpiderMonkey and Rhino, functions have special property __parent__, which is the reference to the activation object (or the global variable object) in which these functions have been created.

Example (SpiderMonkey, Rhino):

var global = this;
var a = 10;
 
function foo() {}
 
alert(foo.__parent__); // global
 
var VO = foo.__parent__;
 
alert(VO.a); // 10
alert(VO === global); // true
 

In the example above we see that function foo is created in the global context and, accordingly, its __parent__ property is set to variable object of the global context, i.e. to the global object.

However, to get the activation object in SpiderMonkey with the same way is not possible: depending on the version, __parent__ for inner function returns either null or global object.

In Rhino, access to the activation object is allowed and available via the same way:

Example (Rhino):

var global = this;
var x = 10;
 
(function foo() {
 
  var y = 20;
 
  // the activation object of the "foo" context
  var AO = (function () {}).__parent__;
 
  print(AO.y); // 20
 
  // __parent__ of the current activation
  // object is already the global object,
  // i.e. the special chain of variable objects is formed,
  // so-called, a scope chain
  print(AO.__parent__ === global); // true
 
  print(AO.__parent__.x); // 10
 
})();
 

Conclusion

In this article we have moved further forward in studying of objects related with execution contexts. I hope the material is useful and has clarified some aspects and ambiguities which, probably, you had before. Further by the plan, the next chapters will be devoted to the Scope chain, Identifier resolution and, as consequence, Closures.

If you have questions, feel free to ask them in comments.

Additional literature


Translated by: Dmitry A. Soshnikov.
Published on: 2010-03-15

Originally written by: Dmitry A. Soshnikov [ru, read »]
Originally published on: 2009-06-27

Write a Comment

Comment

104 Comments

  1. @Dmitry

    Thanks for clarifying the topic for me 🙂

    More detailed info can be found in the ES5.Ch3.1 Lexical Environments.

    I have read it (along with all the other posts here – a few times, actually) but my doubts appeared after that. That’s why I decided to ask you directly. Thanks again.

  2. I was looking for some site where I can get topic wise JS tutorial. And here I am stopped for some time. I hope it’ll complete my search.

  3. @Amit, good overview, but please refer the original sources, since you’re using my abbreviations, and methodology.

  4. Thank you, Dmitry, very useful reading!

  5. Hi Dmitry. Great articles!

    I have a question.
    Assuming we are executing in global context, why is the below result like this?:

    alert(this.x);   //undefined
    alert(x);  //x is not defined
  6. Hi Dmitry,

    In your examples in “Entering the execution context”, since you mentioned, FD will be recorded ahead of variable declaration.

    So, whether it is better to update your case in following way? Switching the position of ‘c’ (variable declaration) and ‘d’ (function declaration), and put ‘d’ ahead of ‘c’ in AO(test).

    Please comment if I am wrong. Thanks.

    Your Code.
    ————————————–
    Let’s see on the example:

    	function test(a, b) {
    	  var c = 10;
    	  function d() {}
    	  var e = function _e() {};
    	  (function x() {});
    	}
    	  
    	test(10); // call

    On entering the test function context with the passed parameter 10, AO is the following:

    	AO(test) = {
    	  a: 10,
    	  b: undefined,
    	  c: undefined,
    	  d: function
    	  e: undefined
    	};

    ————————————–

    Regards 🙂

  7. Hello Dmitry,

    very nice article, I’ve translated this article to german, and I tend to translate, in my spare time, more of your ECMA articles to german.

    The link to my translation of your article
    ECMA-262:Kapitel 2. Variablenobjekt

    I’ve enhanced some aspects, mostly lingu-related, for better understanding, and I’ve tried to keep as close as possible to the original same time.

    If there is something you like to change pls. let me know.

    And if my article fits your requirements I would be lucky to a german ref-link at the top of your article.

    Greetings Andreas Bolm

  8. @Andreas Bolm, great, please add copyright notice on top of the translation with the link to this English version. Also, the signature at the bottom should contain the link to original Russian version for the “ru” link.

  9. @Dmitry Soshnikov

    All done Done. And I’ve put up another German translation of “JavaScript The Core”, this is because I got confused on some aspects of the (other) German translation, and some other minor translation issues.

    Therefore, here is my translation of “JavaScript The Core” ECMA-262: JavaScript Die Grundlagen

    If there is something you like to change pls. let me know.

    And if my (new) article fits your requirements I would be lucky to a german ref-link at the top of your article.

    You may splitt my comment to the appropriate article, if I’am doing something wrong here.

  10. Hello Dmitry,

    I have a discussion at Stack Overflow on topic Multiple global objects in multiple HTML frames. Where some user states that the global object is not accsessible, and furthermore the Global VO does not exist, it merely should be a namespace, he says.

    He said: There is no [global] object: global scope is just a way of conveniently accessing the current window.

    But you said: Global object is the object which is created before entering any execution context; this object exists in the single copy

    So now I’m confused whether there is a Global VO or not?

  11. @Andreas Bolm

    So now I’m confused

    I’m not sure why are use confused 🙂 as well as I’m not sure where the author of that comment refers to. Information on this blog refers to the official ECMAScript specification.

  12. @Dmitry Soshnikov

    Thank you for your time to answer. I came up with the idea that a global object must exist (in ECMA-based JavaScript), by translating your article ECMA-262:JavaScript the Core. Where section “Variable object” read this:

    “Variable object is an abstract concept. In different context types, physically, it’s presented using different object. For example, in the global context the variable object is the global object itself (that’s why we have an ability to refer global variables via property names of the global object).”

    The sentence “…in the global context the variable object is the global object itself…” brought me to this conclusion, albeit the leading sentence says: “Variable object is an abstract concept.” leads to quite the opposite. But collected together the information given here in this article and the other one mentioned, it already brought me back to this conclusion again. Thank you for your precious time.

  13. @Dmitry

    I come again, one more question, please help 🙂

    For the given example in the Code execution

    alert(x); // function

    var x = 10;
    alert(x); // 10

    x = 20;

    function x() {}

    alert(x); // 20


    VO = {};

    VO[‘x’] = <reference to FunctionDeclaration “x”>

    // found var x = 10;
    // if function “x” would not be already defined
    // then “x” be undefined, but in our case
    // variable declaration does not disturb
    // the value of the function with the same name

    VO[‘x’] = <the value is not disturbed, still function>

    I am confusing why VO['x'] = <reference to FunctionDeclaration "x"> ahead of VO['x'] = <the value is not disturbed, still function>
    .

    Doesn’t the JS interpreter scan the source code from up to bottom? Firstly, it encounters var x = 10;, and VO['x'] after assignment is VO['x'] = undefined, which means encountering one variable. Then, interpreter encounters function x() {}, and finds that x is already in VO, while this new one is a function declaration, and it will overwrite previous existing property and value with the same name, thus after assignment, VO is updated to VO['x'] = <reference to FunctionDeclaration "x">.

    Based on your interpretation, it seems interpreter needs scan source code more times.

    Does my understanding right? could you please give more explanation? Thanks.

    Regards 🙂

  14. @Hong, as Variable Instantiation of ES3 spec states, first go parameters, then function declarations, then variables.

    On entering the context, interpreter already has all information which function declarations and variables are defined in the function (from the parsing stage). So it goes and creates functions, then vars names.

    And then it starts execute the code, where assignments happen.

  15. @Dmitry, thanks for your explanation.

    It is now clear that when a declared variable has the same name as a declared function or formal parameter, the variable declaration does not disturb the existing property. How is following situation, when the function declaration has the same name as formal parameter? Will the declared function disturb (overwrite) the existing property added by formal parameter?

  16. @Hong, if you take a look at the Variable Instantiation spec, it says:

    If the variable object already has a property with this name, replace its value and attributes. Semantically, this step must follow the creation of FormalParameterList properties

    I.e. function declarations replace formals which were added first. Then the last stage are vars, which don’t replace function declarations:

    If there is already a property of the variable object with the name of a declared variable, the value of the property and its attributes are not changed. Semantically, this step must follow the creation of the FormalParameterList and FunctionDeclaration properties

  17. Fantastic analysis. A lot of concepts are demystified for me. Now term hoisting is not something as – “you know all variables are raised to top of the scope”. My brain was not satisfied with just that and thanks God I find your blog Dmitry. I will go over all of your blogs and read them 10 times if needed. 😀

  18. Hello Dmitry, I was playing with code a bit and run into this interesting behavior.

    function foo() {
      a = 10; // this leaks to global scope 
      console.log(a); // 10
    }
    foo();
    delete a; //true
    foo(); 

    If I delete a variable, from where foo function pulls out a after I deleted it with delete operator. I think from [[Scope]] of foo, but not sure. How this works, this confuses me a little. Thank you.

  19. Correction, a is not variable , because it is not declared with var keyword, it is a property of the global object, as your article stated.

  20. @blajs, correct, you create a new global “variable” (global property) a each time you call foo. So even if you delete it, it’s being created again in the next foo call on line 2, and hence is available again on line 3. The var is created in the global object, which is reachable from [[scope]] of foo.

  21. Hello! In advance, I apologize for the grammar (i don’t speak english).

    Can you tell me if this example is correct?

    function myFunction(a, b){
       var c = 3;
       function d(){}
    }
    
    myFunction(1, 2);
    
    AO (myFunction) = {
       arguments: {
          "0": 1,
          "1": 2
       }
      
       a: 1,
       b: 2,
       d: <function>
       c: undefined
    }
    

    when I execute:

       Object.getOwnPropertyNames(myFunction);
    

    I get: ["length", "name", "arguments", "caller", "prototype"]
    How should be the abstractly example?
    Many thanks, greetings!

  22. @AndrosGen

    Can you tell me if this example is correct?

    Yes, the example with AO on entering the context is correct. Then, at code execution, variable c from undefined is updated to 3.

    when I execute:

       Object.getOwnPropertyNames(myFunction);
    

    I get: ["length", "name", "arguments", "caller", "prototype"]

    Yeah, method getOwnPropertyNames has nothing to do with the AO of the function. AO — is a mechanism of an implementation, not something you can work in your code. And the getOwnPropertyNames just really returns own property names of an object. Functions have all those properties. If you add another property, it will be returned as well:

    myFunction.myNewProp = 100;
    Object.getOwnPropertyNames(myFunction); // ["length", "name", ... "myNewProp"]
    
  23. Thanks @Dmitry Soshnikov !!

    I think understand it, but, whats happens whit properties?
    i.e

       function myFunction(){
          var x = 1;
          myFunction.y = 2;
       }
    
       AO (myFunction) = {
          arguments: {
             "callee": ,
             "length": 0
          },
          x: undefined
       }
    

    The properties are not part of AO since they are in the function object? because, except global context, the variables declared are not listed as properties when: Object.getOwnPropertyNames(myFunction). Values and references are stored into AO and properties (property x in myFunction), this is correct?

  24. @AndrosGen, correct, properties are not part of the AO, and are just always stored in the function object itself. AO only stores variables, inner functions and parameters of a function.

  25. @Dmitry

    I appreciate your article, is excellent. Thanks for clarify these aspects of JavaScript for beginners like me.

  26. Excellent!
    I have a question.

    variables are declared only with using var keyword.

    And assignments like:

    a = 10;

    just create the new property (but not the variable) of the global object. “Not the variable” is not in the sense that it cannot be changed, but “not the variable” in concept of variables in ECMAScript (which then also become properties of the global object because of VO(globalContext) === global, we remember, yeah?).

    Does ES3 specification say that “if an assignment without var keyword found go ahead and create a property in global object.” ?

    Or

    Is there a logic behind why it only creates a property in the global object not anywhere else in activation object?

    If possible Can you give algorithm/pseudocode of what happens when an assignment happens without var keyword!

  27. @manohar, yes, in ES3 spec the steps are the following:

    1. Assignment happens in http://bclary.com/2004/11/07/#a-11.13.1. There Result(1) is evaluated in Scope chain look up:

    2. Scope chain lookup and identifier resolution: http://bclary.com/2004/11/07/#a-10.1.4: since variable is not found, the Reference type with null base is returned from step 5.

    3. And then PutValue happens here: http://bclary.com/2004/11/07/#a-8.7.2, which says if the base is null, then make base as Global Object, in step 6.

  28. @Tajger, yes, __parent__ was never a standard feature. It’s mostly used to explain relationships betweens environments.

  29. hi Dmitry:
    really nice. but I have a question regarding function declaration hoisting:
    for each function declaration (FunctionDeclaration, FD)
    — a property of the variable object with a name and value of a function-object is created; if the variable object already contains a property with the same name, replace its value and attributes;

    seems if the function declaration located in a block( ie. if condition ), the hoisting value is not thefunction object.

    function test(){
    console.log(fn);
    if( 1 === 1){
    function fn(){};
    }
    console.log(fn);
    }

    I expect both two log out function object, but the first is undefined.

  30. Hi Dmitry,

    Wonderful job, congratulations!

    I would like to go back to one of Hong’s comments, the one posted on 2014-11-23 about the processing sequence of the interpreter. The 2-phase process implies that JS interpreter processes the same piece of code twice, for ‘entering EC’ and ‘code execution’ respectively, and that means JS interpreter does not execute the code immediately as it parses them line by line.

    And another thing – which is more important – is that, what is the principle that an interpreter follows in the case where multiple JS files are imported by the HTML page plus some inline JS code. I came up with two possible options:
    1. JS interpreter completes ‘entering EC’ phase on all the JS code from different JS files, probably including the inline code as well; and then goes into ‘code execution’ phase on all the code it has got.
    2. JS interpreter goes into the ‘code execution’ phase immediately after it completes the ‘entering EC’ phase on the code from the same single file, and it repeats that same process on each imported JS file.

    Well, maybe both options are not the one adopted, but you get my idea. So, what are the exact steps that JS interpreter processes multiple JS files?

    Thanks a lot.

    PS: you know, JS code can make the page to load an extra JS file by manipulating src attribute of nodes, which means the js code can be imported in the current context dynamically, and that makes the process more complex.

  31. @Vincent, thanks for the kind feedback, appreciated!

    “Entering the context”, and “code execution” stages are mostly abstract concepts. They only state the order of some actions happening when we call a function, or execute other code (global, or eval). And how concrete implementations build this, is already another question.

    E.g. if you take an AST interpreter, then a function node would contain all the params, captured (closured) [[Scope]], the body, and the names of all vars, and inner function declarations. So when we evaluate a function call, how we implement these “entering the context”, and “code execution” stages is really up to us (whether its actual two stages, or combined in one stage — as long as we preserve the order in which some events should happen — e.g. all vars should be created with undefined before we actually start execute the code).

    function foo(x) {
      var y = 10;
      return x + y;
    }
    
    foo(5);
    

    In AST foo node could be:

    foo = {
      type: 'function',
      params: ['x'],
      vars: ['y'],
      body: [
        ['var', 'y', '10'],
        ['return', ['+', 'x', 'y']],
      ],
    };
    

    Calling the function:

    // Called with: args = {y: 5}
    
    // 1. Entering the context:
    
    // Activation object/environment.
    const AO = {};
    
    // {x: 5}
    foo.params.forEach(param => AO[param] = args[param]);
    
    // y = undefined
    foo.vars.forEach(variable => AO[variable] = undefined);
    
    ...
    
    // 2. Code execution stage.
    // At this time all vars already in AO.
    
    foo.body.forEach(statement => execute(statement));
    

    As to several files on the same page: they are executed in order they are delivered on the page, and since JS is single-thread — one after another, so from your description point (2) fits more.

  32. Hi Dmitry,

    Thank you for the comprehensive explanation, does the ECMAScript Spec include the structure of the AST as well?

  33. @Vincent, no, the spec only defines abstract algorithms, and semantics. However, there is a Mozilla Parser API widely adopted by several parsers, which produce some “standard” AST. You can take a look on different ASTs in the http://astexplorer.net tool.

  34. the variable declaration does not disturb the existing property.

    (function(a) {
        var a = 20;
        console.log(a); // 20
    })(10);

    I think in above example output would be “20” only for the reason of assignment value to existing property in variable object, right?

  35. @Igor, this means for formal parameters, and functions:

    (function(a) {
    
      var a;
    
      function b() {}
    
      var b;
    
      console.log(a, b); // 10, function
    
    })(10);
    

    As you can see, neither a, nor b variable declarations override (with undefined) the values of existing bindings.

    And in your example, yes, the assignment happening at executing stage, and updates the binding to 20.

  36. Hi Dmitry,

    Amazing article! I have just Googled about you, and am a big fan of your work now.
    Although I understood this article, I still have a small doubt-
    I know this question has been asked already, but could not understand the reason for its output.

    function test(){
       console.log(fn); //outputs undefined
       if(true){
          function fn(){};
       }
       console.log(fn); //outputs function fn(){}
    }
    test();
    

    As per the execution context, the VO(test) object must already have a key named ‘fn’ which holds the reference to the function in memory. Now, when the execution starts, first and second console.log must return the value ‘function fn(){}’.
    Since the output for first console.log is ‘undefined’, this means there is something else which is happening here.

    Would you please help me in understanding this?

  37. @Prateek, thanks, and a good catch! This is due Chrome (and others) started to implement new ES6 semantics with block-scoped functions. The function is assigned inside the block, but the binding (fn name) is hoisted with initial undefined value.

  38. Hi Dmitry,

    Thanks a lot for clarification! Your blogs are really helpful.

    I have fair knowledge of JS but want to know more about the core concepts like- how the V8 engine maintains and executes the code, how memory is managed, where does C++ comes into picture etc.

    Basically, it’s not just about answering what will be the output of a particular code, it’s about answering how and why.

    I would really appreciate if you could take out some time to mentor me in my journey towards gaining core knowledge of JS. Please guide me on where to start, what all to study (any book references) and how can I learn about the functioning of V8 engine etc.
    It would be very helpful if you could provide me the best way in which I could reach this level of clarity in my concepts of JS and V8 Engine.

    Since this comment is not related to your post, you need not publish it. You can reply me on my email- prateektikku@gmail.com

  39. @Prateek Rausa, thanks for the feedback, appreciated, and glad the articles are useful! As to JS implementation, I’d recommend getting generic compiler design knowledge (the “Dragon Book” is pretty good). And then start reading the tutorials, and source code of the V8 engine. However, the overall design details are pretty much as described in this blog, and in the specification: you’ll encounter execution context, stacks, activation records, closures, etc. Good luck with compilers, and virtual machines!

  40. Awesome article.

    Does the [[Scope]] array/linked list only refer to LexicalEnvironments and VariableEnvironments, or can it also refer to ActivationObjects & ArgumentObjects?

    Any noteworthy changes to these variable objects in the latest ecmascript spec?

  41. Hi Dmitry,
    I have a doubt about the “let” keyword in JavaScript. For Eg:

    for(let i=0; i<5 ; i++){
      setTimeout(function(){
        console.log(i);
      }, 1000);
    }

    Gives output: 0 1 2 3 4

    for(var i=0; i<5 ; i++){
      setTimeout(function(){
        console.log(i);
      }, 1000);
    }

    Gives output: 5 5 5 5 5

    I know that "var" is functionally scoped and that "let" is block scoped. Can you please explain a bit in detail as to what exactly is happening in the "let" case. How is the "setTimeout" able to retrieve the correct "i" value in the "let" case?

  42. @Karan Singh Rathod, imaging the let would create a scope per each loop iteration. Something like:

    for (var i = 0; i < 5; i++) {
      // Create scope per-block:
      // The value of `i` is captured
      // via parameter `k`:
      (function(k) {
        setTimeout(function() {
          console.log(k); // 0 1 2 3 4
        }, 1000);
      })(i);
    }
    
  43. Hi, Dmitry, thanks for this great post. I have a question:

    function foo() {
      let bar = 'str';
    }
    foo();
    

    If I use let to declare the variable – bar, is it a property of AO(foo) when this AO is created when entering the foo execution context?
    If so, it looks like: let keyword == var keyword + (the variable before initialization is not allowed to access)?