Update: May 7 2018 - This article was initially written in early 2015 when ES6 was just a fairytale. Now that ECMAScript 2015 is widely used, I thought it would be a good idea to update the article with the changes that ES6 introduced in terms of variable scope.

In addition to var ES6 introduced let and const constructs that create block-scoped variables - variables that exist only in the block of code that encloses their definition.

When running the example below, you'll get an error because a is not defined in the global scope - it only exists in the block between the curly braces.

{
let a = 2;
console.log(a);
}
console.log(a);

In this example, the logged value will be 1 because let a=2; is only available inside the if-block and it is a different variable than the one defined in the first row of the function. Of course, if you want a from within the if statement to point to the original, you just need to remove the let construct.

function x() {
let a = 1;
if(1==1) {
let a = 2;
}
console.log(a)
}

One more thing to consider, when it comes to variables declared with let and const is that for these there is no hoisting. In other words, as opposed to var declarations, this will throw an error:

{
console.log(r);
let r = 1;
}

Also, global variables declared with let and const are not automatically transformed into properties of the global object (window in the case of the browser):

In this post, I will try to explain JavaScript variable scopes and hoisting as well as the many implicit gotchas. To make sure we don't bump into any sudden, unforeseen problems while coding, we must really understand these concepts. Otherwise the trivial task of reading more advanced JavaScript code can prove an overwhelming and most times an unsuccessful attempt.

Below is the original article, written in 2015 which refers only to ES5-style variables.

The scope is the "bucket" in which a variable exists and it is useful because it catalogs the "buckets" from which you can access a variable and if you actually have access to a variable from within a particular "bucket". Variables can be local or global, but the way variables that are defined in a parent scope can be accessed from a child "bucket" can be a little tricky.

Declaring a variable in JavaScript is done using the 'var' keyword. Once declared the respective variable becomes a part of it's parent scope. When defining a variable inside the global scope it is available in local scopes, but a locally defined variable is not available in the global one.

Let's have a look at an example. Executing the code below you will notice that the log inside the function will output the value of the globally defined variable, while the log of the locally defined variable from the global scope will throw an error because the variable is not a part of the global scope and therefor seen as undefined.

The Scope Chain

Things can get really bad if you forget to define a local variable using the "var" keyword. The reason why this can happen is because JavaScript will automatically consider that an undefined variable should be searched for in parent scopes leading up to the global one. In the example below, JavaScript will know that the "a" variable inside someFunction() is local, but in the anotherFunction() it will look for its assignment in parent scopes and it will find it in the parent function.

Hoisting

Hoisting is the process through which variable declarations are automatically "raised" at the top of a function or the global context (if outside a function). Keep in mind that only variable declarations are hoisted, not variable initialization or assignments so, in the case of the code below, the first output will be undefined... but it will not throw out any erros.

In the Window

In browser-based JavaScript, variables defined as a part of the global scope are actually properties of an object called 'window'. Window is the 'container' I was referring to when talking about scopes. This being said, when you want to modify a globally defined variable from a local scope, you can also do that by modifying the respective property of the window object.

As you can see from the above, greet() returns an inner function which is called a "closure". Among other things, closures store a reference to variables and parameters from the enclosing function inside their own local scopes. Referring to our specific example, the parameters who and iterations become local variables in the closure.

This means, that greeting has become a function (the anonymous function returned by greet) and that who and iterations are local variables inside the inner function.

Subsequent calls to greeting() will not invoke greet() again - instead, they will only execute the closure and always return "Hello World!", while incrementing the local iterations variable (not the one defined in the scope of greet(who)).

I have also made a short video tutorial that hopefully explains this better. Have a look!