What is the Execution Context & Stack in JavaScript?

In this post I will take an in-depth look at one of the most fundamental parts of JavaScript, the Execution Context. By the end of this post, you should have a clearer understanding about what the interpreter is trying to do, why some functions / variables can be used before they are declared and how their value is really determined.

What is the Execution Context?

When code is run in JavaScript, the environment in which it is executed is very important, and is evaluated as 1 of the following:

  • Global code – The default envionment where your code is executed for the first time.
  • Function code – Whenever the flow of execution enters a function body.
  • Eval code – Text to be executed inside the internal eval function.

You can read a lot of resources online that refer to scope, and for the purpose of this article to make things easier to understand, let’s think of the term execution context as the envionment / scope the current code is being evaluated in. Now, enough talking, let’s see an example that includes both global and function / local context evaluated code.

Nothing special is going on here, we have 1 global context represented by the purple border and 3 different function contexts represented by the green, blue and orange borders. There can only ever be 1 global context, which can be accessed from any other context in your program.

You can have any number of function contexts, and each function call creates a new context, which creates a private scope where anything declared inside of the function can not be directly accessed from outside the current function scope. In the example above, a function can access a variable declared outside of its current context, but an outside context can not access the variables / functions declared inside. Why does this happen? How exactly is this code evaluated?

Execution Context Stack

The JavaScript interpreter in a browser is implemented as a single thread. What this actually means is that only 1 thing can ever happen at one time in the browser, with other actions or events being queued in what is called the Execution Stack. The diagram below is an abstract view of a single threaded stack:

As we already know, when a browser first loads your script, it enters the global execution context by default. If, in your global code you call a function, the sequence flow of your program enters the function being called, creating a new execution context and pushing that context to the top of the execution stack.

If you call another function inside this current function, the same thing happens. The execution flow of code enters the inner function, which creates a new execution context that is pushed to the top of the existing stack. The browser will always execute the current execution context that sits on top of the stack, and once the function completes executing the current execution context, it will be popped off the top of the stack, returning control to the context below in the current stack. The example below shows a recursive function and the program’s execution stack:

(function foo(i) {
    if (i === 3) {
        return;
    }
    else {
        foo(++i);
    }
}(0));

The code simply calls itself 3 times, incrementing the value of i by 1. Each time the function foo is called, a new execution context is created. Once a context has finished executing, it pops off the stack and control returns to the context below it until the global context is reached again.

There are 5 key points to remember about the execution stack:

  • Single threaded.
  • Synchronous execution.
  • 1 Global context.
  • Infinite function contexts.
  • Each function call creates a new execution context, even a call to itself.

Execution Context in Detail

So we now know that everytime a function is called, a new execution context is created. However, inside the JavaScript interpreter, every call to an execution context has 2 stages:

  1. Creation Stage [when the function is called, but before it executes any code inside]:
    • Create the Scope Chain.
    • Create variables, functions and arguments.
    • Determine the value of "this".
  2. Activation / Code Execution Stage:
    • Assign values, references to functions and interpret / execute code.

It is possible to represent each execution context conceptually as an object with 3 properties:

executionContextObj = {
    scopeChain: { /* variableObject + all parent execution context's variableObject */ },
    variableObject: { /* function arguments / parameters, inner variable and function declarations */ },
    this: {}
}

Activation / Variable Object [AO/VO]

This executionContextObj is created when the function is invoked, but before the actual function has been executed. This is known as stage 1, the Creation Stage. Here, the interpreter creates the executionContextObj by scanning the function for parameters or arguments passed in, local function declarations and local variable declarations. The result of this scan becomes the variableObject in the executionContextObj.

Here is a pseudo-overview of how the interpreter evaluates the code:

  1. Find some code to invoke a function.
  2. Before executing the function code, create the execution context.
  3. Enter the creation stage:
    • Initialize the Scope Chain.
    • Create the variable object:
      • Create the arguments object, check the context for parameters, initialize the name and value and create a reference copy.
      • Scan the context for function declarations:
        • For each function found, create a property in the variable object that is the exact function name, which has a reference pointer to the function in memory.
        • If the function name exists already, the reference pointer value will be overwritten.
      • Scan the context for variable declarations:
        • For each variable declaration found, create a property in the variable object that is the variable name, and initialize the value as undefined.
        • If the variable name already exists in the variable object, do nothing and continue scanning.
    • Determine the value of "this" inside the context.
  4. Activation / Code Execution Stage:
    • Run / interpret the function code in the context and assign variable values as the code is executed line by line.

Let’s look at an example:

function foo(i) {
    var a = 'hello';
    var b = function privateB() {

    };
    function c() {

    }
}

foo(22);

On calling foo(22), the creation stage looks as follows:

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: undefined,
        b: undefined
    },
    this: { ... }
}

As you can see, the creation stage handles defining the names of the properties, not assigning a value to them, with the exception of formal arguments / parameters. Once the creation stage has finished, the flow of execution enters the function and the activation / code execution stage looks like this after the function has finished execution:

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: 'hello',
        b: pointer to function privateB()
    },
    this: { ... }
}

A Word On Hoisting

You can find many resources online defining the term hoisting in JavaScript, explaining that variable and function declarations are hoisted to the top of their function scope. However, none explain in detail why this happens, and armed with your new knowledge about how the interpreter creates the activation object, it is easy to see why. Take the following code example:

​(function() {

    console.log(typeof foo); // function pointer
    console.log(typeof bar); // undefined

    var foo = 'hello',
        bar = function() {
            return 'world';
        };

    function foo() {
        return 'hello';
    }

}());​

The questions we can now answer are:

  • Why can we access foo before we have declared it?
    • If we follow the creation stage, we know the variables have already been created before the activation / code execution stage. So as the function flow started executing, foo had already been defined in the activation object.
  • Foo is declared twice, why is foo shown to be function and not undefined or string?
    • Even though foo is declared twice, we know from the creation stage that functions are created on the activation object before variables, and if the property name already exists on the activation object, we simply bypass the decleration.
    • Therefore, a reference to function foo() is first created on the activation object, and when we get interpreter gets to var foo, we already see the property name foo exists so the code does nothing and proceeds.
  • Why is bar undefined?
    • bar is actually a variable that has a function assignment, and we know the variables are created in the creation stage but they are initialized with the value of undefined.

Summary

Hopefully by now you have a good grasp about how the JavaScript interpreter is evaluating your code. Understanding the execution context and stack allows you to know the reasons behind why your code is evaluating to different values that you had not initially expected.

Do you think knowing the inner workings of the interpreter is too much overhead or a necessity to your JavaScript knowledge ? Does knowing the execution context phase help you write better JavaScript ?

Note: Some people have been asking about closures, callbacks, timeout etc which I will cover in the next post, focusing more on the Scope Chain in relation to the execution context.

Further Reading

Comments

  1. Pingback: JavaScript's Execution Context & Stack, Do You Know It In-Depth ? | CodeSource | Scoop.it

  2. Pingback: What is the Execution Context & Stack in JavaScript? « thoughts…

  3. Pingback: JavaPins

  4. Ahhhh, ok now I can actually conceptualize what hoisting is really doing, on an actual code level basis.

    Thanks for this, looking forward to your scope chain article!

    Reply
  5. Hi david.

    Great article. But I couldn’t get one thing. According to your executionContextObj.
    fooExecutionContext = {
    variableObject: {
    arguments: {
    0: 22,
    length: 1
    },
    i: 22, ........

    arguments and i is stored separately. so they shouldnt interfere with each other. but reality is when within function I update arguments[0] it changes ‘i’ or when I change ‘i’ it changes arguments[0] as well. Would you like to explain?

    Thank you,
    Atif, Pakistan.

    Reply
    • @Muhammad Atif
      Although the arguments and formal parameter names are in separate places in the VO, they are actually a reference to each other, hence why when you change 1 the other changes.

      Reply
  6. Pingback: Fabrizio Giordano | What is the Execution Context & Stack in JavaScript?

  7. Thanks David, very nice article very useful defiantly cleared few doubts please post more articles when you get time.

    Reply
  8. Pingback: Weekly HTML5 Apps Developer Resources, October 3rd 2012 ✩ Mozilla Hacks – the Web developer blog

  9. Pingback: Inspirational reads for developers | Frontend | XING Devblog

  10. @Muhammad Atif,
    And remember this dynamic relationship between arguments object and formal parameters exists only for the parameters which get value during function call. e.g function test(a,b){} is called as test(1) then changing arguments[0] will change value of a and vice-versa but changing arguments[1] will have no effect on value of b.

    Reply
  11. Pingback: Execution context – Creation and Activiation stage | Code and Programming

  12. @David Shariff, Could you explain the execution context for the following code:

    Callbacks = function() {
        var fire = function(context, args){
              console.log("this is private fire() function");  
            },
            self = {
                fireWith:function(context,args){
                    console.log("this is self.fireWith()");
                    fire(context, args);//mark line
                    return this;
                },
                fire:function(){
                    console.log("this is self.fire()");
                    self.fireWith(this, arguments);
                    return this;
                }
            };
            return self;
        }
        var call = new Callbacks();
        call.fire();
    }
    

    Why fire method in self.fireWith() not call self.fire()?

    Reply
    • I’ll take a stab at this, although it’s been over a month:

      1. In your code, fire() in the “self” object doesn’t reference an object method (as in “self.fire” or “this.fire”)
      2. “fire()” is not defined within the context of the self.fireWith() function; it is declared as a variable alongside “self”.

      As a result, calling “fire()” pulls in (var fire). Hope this helps!

      Reply
  13. Pingback: javascript学习资料分享 | JSUED · 视觉设计 · 前端开发 · 交互设计 · 用户研究

  14. Pingback: A Smattering of Selenium #141 « Official Selenium Blog

  15. Pingback: To Arrow Function or not to Arrow Function | Damian Schenkelman

  16. Thanks for the great explanation here! I feel the best way to explain JS scope is visually, and your article really articulates this very well.

    Reply
  17. Pingback: javascript学习资料分享 – 宋兵甲,炮灰乙,流氓丙,土匪丁,Just_So_so_Cel,YoLuo

  18. Pingback: JS Execution Context – Closures | Java

  19. Great Article ,simply great.Also many thanks to David for this wonderful article. Thanks Again, I’m waiting for more! heart touching articles.Cleared up every confusions.

    Reply
  20. Pingback: JavaScript Execution Context - Yusuf Aytaş

      • i am currently reading ” javascript professional for web developer 3rd edition ” book there it talks about functions internal [[scope]] property which i dont understand very well i thought you can explain it better, and yes i have read your article on scope chain its very good and well written but there is no mention of function’s [[scope]] property

        Reply
  21. Pingback: Execution Contexts and Scope Chains in Javascriptdavidrogers.id.au - blog | davidrogers.id.au – blog

  22. Pingback: The Executioner: the JavaScript execution context and how to defeat it - Zen Code

  23. Pingback: Professional Javascript For Web Developers – 复习笔记(Variables, Scope, and Memory) | Ian's Blog

    • This article is referring to ECMA:262-3 Section 10.2.3

      “When control enters an execution context, the scope chain is created and initialised, variable instantiation is performed, and the this value is determined.”

      Reply
  24. does variable is hoisted first or function?

    (function foo(){
    return boo;
    function boo(){return this;}
    var boo = 5;
    })()

    or

    (function foo(){
    return boo;
    var boo = 5;
    function boo(){return this;}

    })()

    both returns (in chrome),

    function boo(){return this;}

    Reply
  25. Pingback: Javascript: why variable declaration can always overwritten function declaration?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite

  26. Awesome article. Got a detailed understanding on many concepts which haunted me for long time. Great work dude..

    Reply
  27. Hi David,

    would u please explain why last foo type is string? am I misunderstanding something

    console.log(typeof foo); // function pointer
    var foo = ‘hello’;
    console.log(typeof foo); // string
    function foo() {
    alert(1);
    };
    console.log(typeof foo); // string

    Reply
  28. Pingback: Seven JavaScript Quirks I Wish I’d Known About | Telerik Developer Network

  29. Pingback: Coding Serf - 【译】我希望我尽早知道的7个JavaScript怪癖

  30. Pingback: [Перевод] Всё, что вы должны знать о прототипах, замыканиях и производительности

  31. Pingback: 我希望自己尽早知道的7个JavaScript怪癖 | 笨笨时光机

  32. Pingback: 7个JavaScript怪癖 « WEB开发分享

Leave a Reply

Your email address will not be published. Required fields are marked *


6 − two =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>