JavaScript Scoping and Hoisting Translation

Source: Internet
Author: User

Do you know what alert will output after the following JavaScript code is executed? Copy codeCode: var foo = 1;
Function bar (){
If (! Foo ){
Var foo = 10;
}
Alert (foo );
}
Bar ();

If the answer is "10", you will be even more confused:
[/Code]
Var a = 1;
Function B (){
A = 10;
Return;
Function (){}
}
B ();
Alert ();
[/Code]
The browser displays alert "1 ". So what's wrong? Although this seems strange, dangerous, and confusing, it is actually a powerful expressive feature of the language. I don't know if there is a standard to define such behavior, but I like to describe it with "hoisting. This article tries to explain this mechanism, but first, let's have some necessary understanding of JavaScript scoping.
Scoping in JavaScript
Scoping is one of the most confusing parts for beginners of JavaScript. In fact, it is not just a newbie. I have met or many experienced JavaScript programmers who cannot fully understand scoping. The reason why JavaScript scoping is so complex is that it looks very much like a member of the C-series language. See the following C program:Copy codeThe Code is as follows: # 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
}

The output of this program is 1, 2, and 1. This is because the C system language has a block-level scope. When a block is entered, it is like an if statement, new variables will be declared in this block-level scope, and these variables will not affect the external scope. But JavaScript is not like this. Try the following code in Firebug:Copy codeCode: var x = 1;
Console. log (x); // 1
If (true ){
Var x = 2;
Console. log (x); // 2
}
Console. log (x); // 2

In this Code, Firebug shows 1, 2. This is because JavaScript is a function-level scope ). This is completely different from the C language. Block, just like the if statement, does not create a new scope. Only functions can create a new scope.
For most programmers familiar with C, C ++, C #, or Java, this is unexpected and not to be seen. Fortunately, due to the flexibility of JavaScript Functions, we have a solution to this problem. If you must create a temporary scope in the function, do the following:Copy codeThe Code is as follows: function foo (){
Var x = 1;
If (x ){
(Function (){
Var x = 2;
// Some other code
}());
}
// X is still 1.
}

This is indeed very flexible. It is used wherever a temporary scope needs to be created, not just a block. However, I strongly recommend that you take some time to understand JavaScript scoping. It is really powerful, and it is also one of my favorite language features. If you understand scoping well, it will be easier to understand hoisting.
Declarations, Names, and Hoisting
In JavaScript, the name in a scope has the following four types:
1. Language-defined: All scopes include this and arguments by default.
2. Formal parameters: the named parameters of the function enter the scope of the function body.
3. Function decalrations: in the form of function foo.
4. Variable Declaration (Variable declarations): in the form of var foo.
Function declarations and variable declarations are implicitly promoted (hoist) by the JavaScript interpreter to the top of their scopes. Obviously, the language definition and function parameters are at the top of the scope. This is like the following code:Copy codeThe Code is as follows: function foo (){
Bar ();
Var x = 1;
}

It is actually interpreted as follows:Copy codeThe Code is as follows: function foo (){
Var x;
Bar ();
X = 1;
}

The result is that no matter whether the statement is executed or not. The following two pieces of code are equivalent:Copy codeThe Code is as follows: function foo (){
If (false ){
Var x = 1;
}
Return;
Var y = 1;
}
Function foo (){
Var x, y;
If (false ){
X = 1;
}
Return;
Y = 1;
}

Note that the value assignment part of the declaration is not promoted (hoist ). Only the declared name is promoted. This is different from the function declaration. In the function declaration, the entire function body will also be upgraded. But remember, there are two methods to declare a function. Consider the following JavaScript code:Copy codeThe Code is as follows: function test (){
Foo (); // TypeError "foo is not a function"
Bar (); // "this will run! "
Var foo = function () {// The function expression is assigned to the variable 'foo'
Alert ("this won't run! ");
}
Function bar () {// function declaration named 'bar'
Alert ("this will run! ");
}
}
Test ();

Here, only the function declaration method is upgraded together with the function body, while the function expression only promotes the name. The function body is assigned only when the value assignment statement is executed.
The above includes all the basics of hoisting. It seems not so complicated or confusing, right. However, this is JavaScript. In some special cases, it will always be a little complicated.
Name Resolution Order
The most important special case to remember is the name resolution order ). There are four ways to remember a name to enter a scope. The order listed above is the order in which they are parsed. In general, if a name has been defined, it will never be overwritten by another name with an unused attribute. This means that the function declaration has a higher priority than the variable declaration. However, this does not mean that the assignment of this name is invalid, but the declared part will be ignored. However, there are several exceptions:
The built-in name arguments is a little weird. It seems that it is declared after the form parameter and before the function declaration. This means that the parameter named arguments has a higher priority than the built-in arguments, even if the parameter is undefined. This is a bad feature. Do not use arguments as the parameter.
Any attempt to use this as an identifier will cause a syntax error, which is a good feature.
If multiple parameters with the same name exist, the parameter at the end of the list has the highest priority, even if it is undefined.
Name Function Expressions
You can define a name for a function in a function expression, just like a statement declared by a function. But this does not make it a function declaration, and the name will not be introduced into the scope, and the function body will not be upgraded (hoist ). Here are some codes to illustrate what I mean:Copy codeCode: foo (); // TypeError "foo is not a function"
Bar (); // valid
Baz (); // TypeError "baz is not a function"
Spam (); // ReferenceError "spam is not defined"
Var foo = function () {}; // anonymous function expression ('foo' is promoted)
Function bar () {}; // function declaration ('bar' and function body are promoted)
Var baz = function spam () {}; // name the function expression (only 'baz' is promoted)
Foo (); // valid
Bar (); // valid
Baz (); // valid
Spam (); // ReferenceError "spam is not defined"

How to Code With This Knowledge
Now that you understand the scope and improvement, what does this mean for writing JavaScript code? The most important one is to always use the var statement when declaring a variable. I strongly recommend that you use only one var at the top of each scope. If you force yourself to do this, you will never be troubled by problems related to improvement. However, this will make it more difficult to track which variables are actually declared in the current scope. I suggest using the onevar option in JSLint. If you make all the preceding suggestions, your code looks like the following:Copy codeThe Code is as follows:/* jslint onevar: true [...] */
Function foo (a, B, c ){
Var x = 1,
Bar,
Baz = "something ";
}

What the Standard Says
I found that it is always useful to directly refer to ECMAScript Standard (pdf) to understand how these things work. The following is an excerpt from the variable description and scope (section 12.2.2 ):
If the variable statement occurs inside a FunctionDeclaration, the variables are defined with function-local scope in that function, as described in section 10.1.3. otherwise, they are defined with global scope (that is, they are created as members of the global object, as described in section 10.1.3) using property attributes {DontDelete }. variables are created when the execution scope is entered. A Block does not define a new execution scope. only Program and FunctionDeclaration produce a new scope. variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.

I hope this article will give some inspiration to JavaScript programmers who are most confused. I try my best to write it comprehensively so as not to cause more confusion. If I have written an error or missed something important, please let me know.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.