Random memo

Scope of execution context in Javascript

September 28, 2018

Every function in Javascript creates Execution Context(EC) when it is called. Lexical scope of a function’s execution context is determined where it is defined, not where it runs. Let’s see how lexical scope of a function’s EC works in javascript.

function a() {
  console.log("a() called ", foo)
}

function x() {
  function b() {
    console.log("b() called ", foo)
  }
  var foo = 1
  b()
  a()
}

var foo = 0

a()
x()

Here, function a’s lexical scope of EC is functions a's EC -> global EC. However, functions b’s scope of EC is b's EC -> x's EC -> global EC.
Output of the code above:

a() called  0
b() called  1
a() called  0

As in the output, lexical scope of EC for calling a() inside function x is not a’s EC -> x’s EC -> global EC. It is a's EC -> global EC. So, it sees foo = 0 defined in global EC, not foo = 1 in x’s EC.

If you omit var inside functions x like below:

function a() {
  console.log("a() called ", foo)
}

function x() {
  function b() {
    console.log("b() called ", foo)
  }
  foo = 1
  b()
  a()
}

var foo = 0

a()
x()

foo is refered to one in global EC by default. So output would be:

a() called  0
b() called  1
a() called  1

This can be problematic since it causes global side-effect. This usage is legal even if you use use strict like this:

"use strict"

function a() {
  console.log("a() called ", foo)
}

function x() {
  function b() {
    console.log("b() called ", foo)
  }
  foo = 1
  b()
  a()
}

var foo = 0

a()
x()

Code snippet above runs with no error. That’s because foo is defined in the top-level scope in the course of tracing the definition of foo. Developer’s care should be taken.

On the other hand, misterious behavior like this:

function a() {
  console.log("a() called ", foo)
}

function x() {
  function b() {
    console.log("b() called ", foo)
  }
  foo = 1
  b()
  a()
}

x()

can happen with output

b() called  1
a() called  1

Does a() called inside function x refer foo = 1 in function x’s execution context? No.

foo = 1 without var inside function x implicitly define foo in the global context. That’s how a() got foo = 1.

use strict can help prevent this case by throwing error, like so:

"use strict"

function a() {
  console.log("a() called ", foo)
}

function x() {
  function b() {
    console.log("b() called ", foo)
  }
  foo = 1
  b()
  a()
}

x()

ReferenceError: foo is not defined


Sangche

Written by Sangche. Github