A Quick Introduction to the JavaScript Call Stack

Image via Williams-Sonoma

What is the Call Stack?

How your code appears to a web browser before interpretation

To paraphrase Thomas Edison, “[JavaScript] without execution is hallucination” — meaning we can write all the brilliant code we want, but it does not actually DO anything until executed. When we run (or invoke) code, we create an Execution Context (the first, or default is the Global Execution Context) and a new context is created thereafter for each new scope encountered. The execution contexts are then pushed onto the call stack. MDN defines the call stack as “a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions…”. The call stack operates one line at a time and adheres to the LIFO — last in, first out — principle. Once the call stack is created, the script on top is executed and then popped off until the final script is reached, completing the run of the program.

Call stack formation for code on left

Why should you care?

Unlike other concepts in JavaScript, say Higher Order Functions, one need not understand the mechanics of the call stack in order for it to operate. So why bother learning about it at all? Well, having a solid understanding of the call stack and execution context makes learning more advanced topics like scopes and closures easier. Mastering the call stack also makes debugging your code easier as well. When you can start thinking in terms of code execution and interpretation, pinpointing problems becomes more intuitive and less like trying to find a needle in a hay stack.

Call Stack, Step by Step

What happens when the above code runs:

  1. The first function call is foo() which acts as our entry-point and creates the first execution context added to the call stack
  2. foo() then calls bar() — bar() gets added on to call stack
  3. bar() returns to print “Hello from bar” to the console
  4. bar() has finished executing and is then popped off the call stack
  5. The second execution context of foo() is reached
  6. foo() prints “Goodbye from foo” to console
  7. foo() has finished executing and is popped off the call stack — clearing the stack and terminating the program