JavaScript Variable Scoping Rules

23 Jul

The scoping of variables is JavaScript (or ECMAScript to be pedantic) can be very confusing and unintuitive for those coming from languages like C/C++ or Java. In this post I attempt to distill the rules of scoping variables in JavaScript, and show how they work by example.

Global and Local Scope

There are two basic scopes for a variable in JavaScript: global and local. Global variables are defined outside of any function, are a property of the window object (when executed in a browser), or declared without the var keyword. Local variables are defined inside of a function using the var keyword.

var foo = 'a'; // global variable
bar = 'b'; // global variable

// global variable
var myFun = function() {
  var foo = 'x'; // local variable, "hides" the global one
  bar = 'y'; // global variable, reassigns the global: b -> y

  (function() {
    var baz = 1; // local variable
    foo = 2; // walks the scope hierarchy, reassigns local: x -> 2
    blah = 3; // global variable
  }())
}

myFun();

// baz is undefined here

See this code in action.

Most programmers coming from C/C++ or Java are going to complain about the fact that you can easily think you’re declaring a local variable, but actually declaring a global one. Using the new strict mode can help to catch these errors.

Block-Level Scoping

JavaScript does NOT have block-level scoping. What this means is that variables declared inside of a loop or conditional branch can be accessed outside of this block.

var j = 'foo';

// i is not scoped to the for loop
for(var i=0; i < 3; ++i) {
  var j = 'bar'; // this reassigns j: foo -> bar
}

See this code in action.

One place where JavaScript does bind variables on a block-level is in try-catch blocks. The exception that is thrown is bound ONLY to the catch block.

It should also be noted that ECMAScript 6 introduces support for block-level scoping through the use of two keywords: let and const. Changing our previous example:

var j = 'foo';

// i is not scoped to the for loop
for(var i=0; i < 3; ++i) {
  let k = 'bar'; // variable scoped to the for loop only
}

Variable Hoisting

Probably the most confusing part of JavaScript variable scoping is that JavaScript hoists variables. What this means is that all declarations are moved to the first line of the function they are in. So for example

var foo = 'a'; // create a global variable and assign it 'a'

fun(); // call our function fun(); this is not an error

function fun() {
  alert(foo); // does not display what you might think

  if(true) {
    var foo = 'b'; // create a local variable and assign it 'b'
  }
}

… actually becomes:

var foo = 'a'; // create a global variable and assign it 'a'

function fun() {
  var foo;

  alert(foo); // does not display what you might think

  if(true) {
    foo = 'a'; // assigns the local variable: undefined -> 'a'
  }
}

fun(); // call our function fun();

See this code in action.

As a consequence of variable hoisting, the alert will actually show undefined instead of the expected a. This can be VERY tricky, so one must take great care in defining variables and functions. It is often best to define all variables at the beginning of a function (not in a loop or conditional block) to avoid these types of mistakes.

Leave a Reply

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