JavaScript
Raiting:
5

Scope in JavaScript, “hoisting” of variables and function declarations


Do you know what value produce this code in JavaScript?

var foo = 1;
function bar() {
if (! foo) {
var foo = 10;
}
alert(foo);
}
bar();

If you are surprised what will produce "10", then the following code confuse you at all:


var a = 1;
function b () {
a = 10;
return;
function a() {}
}
b();
alert(a);

In this case, the browser produces "1". So what exactly is going on? Although this behavior seems strange, dangerous and confusing, in fact it is very powerful and significant tool JavaScript. There is a name for term for such behavior, which is “hoisting”. This article will try to explain the mechanism of the language, but first let us talk about scope in JavaScript.

Scope in JavaScript


One of the reasons that lead to confusion the beginners and experienced developers is a scope. There are many experienced JavaScript developers, who do not understand the mechanism of scope in JavaScript. The reason is that JavaScript looks very similar to any other C-like language.
Let us consider the following code in C:


#include <stdio.h>
int main() {
int x = 1;
printf("%d,", x); // 1
if (1) {
int x = 2;
printf("%d,", x); // 2
}
printf("%d\n", x); // 1
}

This program will produce 1, 2, 1, because C and all other C-like languages realize the scope on block level code. When a new block of code is executed such as the condition, the new variables that are declared in it will not affect the variables of the outer scope.
But it is not in the case of JavaScript. Let us try to run this code in Firebug:


var x = 1;
console.log(x); // 1
if (true) {
var x = 2;
console.log (x); // 2
}
console.log (x); // 2

This time will be produced the numbers 1, 2, 2. This is due to the fact that in JavaScript is used a scope on the level of functions. This is not what we are used to see in the programming languages like C. The blocks of code do not create a new scope like the one that goes after if. Only functions create the new scopes.
For many programmers that are used to C, C + +, C# or Java such behavior is very unexpected and unpleasant. Fortunately, thanks to the functions flexibility of JavaScript, we can avoid this problem. In order to create a temporary scope inside of a function, it is enough to do the following:


function foo() {
var x = 1;
if (x) {
(function () {
var x = 2;
// some code
}());
}
// x is still 1.
}

This approach is flexible and can be used anywhere, where we need a temporary scope and not only within blocks of code. But we still need to take the time to understand the realization of scope in JavaScript. This is quite a powerful language feature. If we understand the scope, it will be easier to understand the "hoisting" of variables and the function declarations.

Declarations, naming, and "hoisting" of variables and the functions


There are four basic ways of the appearance of an identifier in scope in JavaScript:

1. Internal mechanisms of language: for example, in all scopes are available “this” and “arguments”.
2. Formal parameters: the functions can be named as the formal parameters, which scope is limited to the function body.
3. Function declarations: declared in the form of function foo() {}.
4. Variable declarations: for example, var foo;.

JavaScript interpreter is always moves invisibly ("hoisting") the function declarations and variables in the top of the scope. The formal parameters of functions and built-in variables of language, obviously already are at beginning. This means that this code:


function foo() {
bar();
var x = 1;
}

actually is interpreted as:


function foo() {
var x;
bar();
x = 1;
},

It turns out that it does not matter, would it ever made a string in which the announcement. The next two functions are equivalent:


function foo() {
if (false) {
var x = 1;
}
return;
var y = 1;
}
function foo() {
var x, y;
if (false) {
x = 1;
}
return;
y = 1;
}

Notice that the variable value assignment does not hoist along with their declaration, only variable declarations are hoisted. In the case of functions, the whole function is hoisted as a whole. There are two basic ways to declare a function, let us examine them:


function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function() {// function expression is assigned to a local variable 'foo'
alert("this won't run!");
}
function bar() {// function declaration is with a name 'bar'
alert("this will run!");
}
}
test();

In this case, only the function bar is hoisted. Identifier “foo” also is hoisted, but not an anonymous function - it stays in place.

Here are described the main points of "hoisting" of variables and functions. Of course, JavaScript would not be itself if there were not the special cases in which everything is a bit more complicated.

Name resolution


The most important case that should be kept in mind is the order of name resolution. There are four ways of the appearance of identifiers in the scope. In the same order goes the name resolution. In general, if a name is already defined, it will never be redefined by another entity with the same name. That is the function declaration has a priority over the declarations of the variable with the same name. But this does not mean that the value of a variable assignment does not replace the function, just its definition will be ignored.
There are some exceptions:

• Built-in ID “arguments” behaves strangely. It seemed to be declared immediately after the formal arguments to functions and before the function declarations. This means that if the function has a formal “arguments”, it will have priority over the built-in, even if it will not be given when the function is called. This is a bad feature of JavaScript. Do not use a formal argument with name arguments.
• If we try to use “this” as an identifier, there will be an error SyntaxError. This is a nice feature.
• If a few of them have the same name in the formal parameter function list, the parameter that is referred as last has a priority. Even, if it was not given when the function is called.

Functional expressions that are named


We can give the names to the functions that are defined by the functional expressions using the syntax of the functions. This does not lead to the function declaration, so the function name is not added to the scope, nor hoisted with the body to the top of the function scope. Here are a few lines to illustrate it:


foo(); // TypeError "foo is not a function"
bar(); // works
baz(); // TypeError "baz is not a function"
spam(); // ReferenceError "spam is not defined"

var foo = function () {}; // anonymous function expression (hoisted 'foo')
function bar () {}; // function declaration (hoisted 'bar' and the function body)
var baz = function spam() {}; // named function expression (hoisted only 'baz')

foo(); // works
bar(); // works
baz(); // works
spam(); // ReferenceError "spam is not defined"

How do we write a code if we have such knowledge


So now, we understand the scope, "hoisting" of variables and the function declarations. What does this mean to write a code in JavaScript? The most important thing is always declare the variables using the var. We need to have exactly one var for a scope and it should be located at the beginning, so we will never have problems associated with "hoisting". However, this can lead to complicacy to keep track of variables that are declared in the current scope. It is recommended using JSLint with enabled option onevar. If we do it all, the code would look like this:


/*Jslint onevar: true [...] */
function foo(a, b, c) {
var x = 1,
bar,
baz = "something";
}

What does the standard say?


It is sufficiently useful go directly to the ECMAScript, in order to understand how everything works. That is what it says about the declaration of variables and scope (section 12.2.2):
If the instruction of variable has met inside of function declaration, the variables are declared inside a local scope for the function as described in a section 10.1.3. Otherwise, they are declared in global scope (i.e. they are created as the fields of the global object as described in a section 10.1.3) using the property attributes {DontDelete}. Variables are created when there is an input in field of executing. Block does not define a new execution scope. Only the program and function declaration create the new scope. Variables are initialized when is created the value “undefined”. For variable that has the defined initializer is set value of its assignment expression at the time of executing the variable instruction, but not at the time when the variable has been creating.

This article has shed some light on the peculiarity of JavaScript, which confuses many developers so often.
Sparks 30 october 2011, 17:14
Vote for this post
Bring it to the Main Page
 

Comments

0 jporter892 April 27, 2012, 8:35
Great article!
0 Debo September 4, 2016, 9:51
Is this a copy of this?
http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

Leave a Reply

B
I
U
S
Help
Avaible tags
  • <b>...</b>highlighting important text on the page in bold
  • <i>..</i>highlighting important text on the page in italic
  • <u>...</u>allocated with tag <u> text shownas underlined
  • <s>...</s>allocated with tag <s> text shown as strikethrough
  • <sup>...</sup>, <sub>...</sub>text in the tag <sup> appears as a superscript, <sub> - subscript
  • <blockquote>...</blockquote>For  highlight citation, use the tag <blockquote>
  • <code lang="lang">...</code>highlighting the program code (supported by bash, cpp, cs, css, xml, html, java, javascript, lisp, lua, php, perl, python, ruby, sql, scala, text)
  • <a href="http://...">...</a>link, specify the desired Internet address in the href attribute
  • <img src="http://..." alt="text" />specify the full path of image in the src attribute