Initialization of functions and variables

  1. Instantiation of top-level variables
  2. Function variables
  3. Blocks do not have scope

Mechanics of variables and functions in JavaScript is completely different from most other languages.

Advanced topics become easy to grasp once you know how it works.

In JavaScript, all local variables and functions are properties of the special internal object, called LexicalEnvironment.

The top-level LexicalEnvironment in browser is window. It is also called a global object.

Instantiation of top-level variables

When the script is going to be executed, there is a pre-processing stage called variables instantiation.

  1. First, the interpreter scans the code for <a href="/tutorial/functions-declarations-and-expressions">Function Declarations</a>, which are declared as function name {...} in the main code flow.

    It takes every declaration, creates the function from it and puts it into window.

    For example, consider the code:

    var a = 5
    
    function f(arg) { alert('f:'+arg) }
    
    var g = function(arg) { alert('g:'+arg) }
    

    At this stage, the browser finds function f, creates the function and stores it as window.f:

    // 1. Function Declarations are initialized before the code is executed.
    // so, prior to first line we have: window = { f: function }
    
    var a = 5
    
    *!*function f(arg)*/!* { alert('f:'+arg) } // <-- FunctionDeclaration
    
    var g = function(arg) { alert('g:'+arg) }
    

    As a side effect, f can be called before it is declared:

    f()
    function f() { alert('ok') }
    

  2. Second, the interpreter scans for var declarations and creates window properties. Assignments are not executed at this stage. All variables start as undefined.

    // 1. Function declarations are initialized before the code is executed.
    // window = { f: function }
    
    // 2. Variables are added as window properties.
    // window = { f: function, a: undefined, g: undefined }
    
    *!*var a*/!* = 5   // <-- var
    
    function f(arg) { alert('f:'+arg) }
    
    *!*var g*/!* = function(arg) { alert('g:'+arg) } // <-- var
    

    The value of g is a function expression, but the interpreter doesn’t care. It creates variables, but doesn’t assign them.

    So to sum:

    1. FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
    2. Variables start as undefined.
    3. All assignments happen later, when the execution reaches them.

    As a side effect, it is impossible to have a variable and a function with the same name.

  3. Then the code starts running.

    When a variable or function is accessed, the interpreter gets it from window:

    alert("a" in window) // true, because window.a exists
    alert(a) // undefined, because assignment happens below
    alert(f) // function, because it is Function Declaration
    alert(g) // undefined, because assignment happens below
    
    var a = 5  
    
    function f() { /*...*/ } 
    var g = function() { /*...*/ }
    

  4. After the assignments, a becomes 5 and g becomes a function. In the code below, alerts are moved below. Note the difference:

    var a = 5  
    var g = function() { /*...*/ } 
    
    alert(a) // 5
    alert(g) // function
    

    If a variable is not declared with var, then, of course, it doesn’t get created at initialization stage. The interpreter won’t see it:

    alert("b" in window) // false, there is no window.b
    alert(b) // error, b is not defined
    
    b = 5
    

    But after the assignment, b becomes the regular variable window.b as if it were declared:

    b = 5 
    
    alert("b" in window) // true, there is window.b = 5
    

  5. What will be the result?

    if ("a" in window) {
        var a = 1
    }
    alert(a)
    

    Open solution
    Solution

    The answer is 1.

    Let’s trace the code to see why.

    1. At initialization stage, window.a is created:
      // window = {a:undefined}
      
      if ("a" in window) {
          var a = 1
      }
      alert(a)
      
    2. "a" in window is true.
      // window = {a:undefined}
      
      if (true) {
          var a = 1
      }
      alert(a)
      

      So, if is executed and hence value of a becomes 1.


    What will be the result (no var before a)?

    if ("a" in window) {
        a = 1
    }
    alert(a)
    

    Open solution
    Solution

    The answer is “Error: no such variable”, because there is no variable a at the time of "a" in window check.

    So, the if branch does not execute and there is no a at the time of alert.

    Function variables

    When the function runs, on every function call, the new LexicalEnvironment is created and populated with arguments, variables and nested function declarations.

    This object is used internally to read/write variables. Unlike window, the LexicalEnvironment of a function is not available for direct access.

    Let’s consider the details of execution for the following function:

    function sayHi(name) {
      var phrase = "Hi, " + name
      alert(phrase)
    }
    
    sayHi('John')
    

    1. When the interpreter is preparing to start function code execution, before the first line is run, an empty LexicalEnvironment is created and populated with arguments, local variables and nested functions.

      function sayHi(name) {
      // LexicalEnvironment = { name: 'John', phrase: undefined }
        var phrase = "Hi, " + name
        alert(phrase)
      }
      
      sayHi('John')
      

      Naturally, arguments have the starting value, but the local variables don’t.

    2. Then the function code runs, eventually assignments are executed.

      A variable assignment internally means that the corresponding property of the LexicalEnvironment gets a new value.

      So, phrase = "Hi, "+name changes the LexicalEnvironment:

      function sayHi(name) {
      // LexicalEnvironment = { name: 'John', phrase: undefined }
        var phrase = "Hi, " + name
      // LexicalEnvironment = { name: 'John', phrase: 'Hi, John'}
        alert(phrase)
      }
      
      sayHi('John')
      

      The last line alert(phrase) searches the phrase in LexicalEnvironment and outputs it’s value.

    3. At the end of execution, the LexicalEnvironment is usually junked with all its contents, because the variables are no longer needed. But (as we’ll see) it’s not always like that.

    Specification peculiarities

    If we look into the recent ECMA-262 specification, there are actually two objects.

    The first is a VariableEnvironment object, which is actually populated by variables and functions, declared by FunctionDeclaration, and then becomes immutable.

    The second is a LexicalEnvironment object, which is almost same as VariableEnvironment, but it is actually used during the execution.

    A more formal description can be found in the ECMA-262 standard, sections 10.2-10.5 and 13.

    It is also noted that in JavaScript implementations, these two objects can be merged into one. So, we evade irrelevant details and use the term LexicalEnvironment everywhere.

    Blocks do not have scope

    There is no difference between the following:

    var i = 1
    {
      i = 5
    }
    

    …And the following
    i = 1
    {
      var i = 5
    }
    

    All var declarations are processed before the execution in in both cases.

    Unlike languages like Java, C etc, variables in JavaScript survive after a loop.

    That’s again, because their scope is a function.

    for(var i=0; i<5; i++) { }
    
    alert(i) // 5, variable survives and keeps value
    

    Declaring a variable in the loop is convenient, but doesn’t make the loop it’s scope.

    What this test is going to alert? Why? (Don’t run it before you answer, please)

    function test() {
      
      alert(window)
    
      var window = 5
    }
    test()
    

    Open solution
    Solution

    The var directive is processed on the pre-execution stage.

    So, window becomes a local variable before it comes to alert:

    LexicalEnvironment = {
      window: undefined
    }
    

    So when the execution starts and reaches first line, variable window exists and is undefined.

    How do you think, what will be the output? Why?

    var value = 0
    
    function f() {  
      if (1) {
        value = 'yes'
      } else {
        var value = 'no'
      }
    
      alert(value)
    }
    
    f()
    

    Open solution
    Solution

    The var directive is processed and created as LexicalEnvironment property at pre-execution stage.

    So, the line value='yes' performs an assignment to the local variable, and the last alert outputs 'yes'.

Tutorial

Donate

Donate to this project