Translated from: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
Do you know what the result is if the following code executes?
var foo = 1; function Bar () { if(! Foo) { var foo = ten; } Alert (foo);} Bar ();
The result of this operation is 10. If you are surprised by the results, take a look at the following example:
var a =1;function B () { a = ten; return; function A () {}}b (); alert (a);
The result of this code is 1. So what exactly happened? While this may seem strange, dangerous and confusing, it is actually a powerful and expressive feature of language. I don't know if this particular behavior has a standard name, but I have thought of the word "ascension". This article will try to illustrate this mechanism, but first let's take a closer look at the scope of JavaScript.
Scope of JavaScript
One of the most confusing reasons for JavaScript beginners is scoping. In fact, this is not just beginners. I met a lot of experienced JavaScript programmers who don't fully understand scoping. The reason for this confusion in JavaScript is that it looks like a C-series language. Consider the following C program:
#include <stdio.h>intMain () {intx =1; printf ("%d,", x);//1 if(1) { intx =2; printf ("%d,", x);//2} printf ("%d\n", x);//1}
The output of this program will be 1,2,1. This is because the rest of the C and C Series have block-level scopes. When a control enters a block, such as an if statement, a new variable can be declared within that scope without affecting the outer scope. JavaScript is not so. In Firebug, try the following:
var x = 1// 1if (true) { var x = 2; // 2// 2
In this case, Firebug will display 1,2,2. This is because JavaScript has a functional scope. This is very different from the C series. A block, such as an if statement, does not create a new scope. Only the function creates a new scope.
This is unexpected and undesirable for programmers accustomed to languages such as C,c ++,c# or Java. Fortunately, there is a workaround due to the flexibility of JavaScript functionality. If you must create a staging scope within a function, do the following:
function foo () { var x = 1; if (x) { (function () { var x = 2; // Some other code }()); } // x is still 1.}
This approach is actually very flexible and can be used anywhere a temporary scope is needed, not just in a block statement. However, I strongly recommend that you take the time to really understand and appreciate the JavaScript scope. This is very powerful and is one of my favorite language features. If you understand the scope, hosting will be more meaningful.
Hosting
In JavaScript, a name enters a range in one of four basic ways:
Language definition: By default, all scopes give the name of this and parameter.
Formal parameters: Functions can have a named form parameter, which is the body of the function.
Function declarations: These are the forms of the function foo () {}.
Variable declaration: These forms are var foo;
function declarations and variable declarations are always moved by the JavaScript interpreter ("hosting") to the top of its containing range. The names of functional parameters and language definitions are clearly already there. This means that the code:
function foo () { bar (); var x = 1;}
This is actually the case:
function foo () { var x; Bar (); = 1;}
It turns out that it is not important whether the row containing the declaration will be executed. The following two functions are equivalent:
function foo () { if (false var x = 1; return ; var y = 1;} function foo () { var x, y; if (false = 1 return ; Y = 1;}
Please note that the transfer portion of the declaration has not been suspended. Only this name has been hoisted. function declaration is not the case, the entire function body will also be suspended. Keep in mind, however, that there are two normal ways to declare a function. Consider the following javascript:
function Test () { /// TypeError ' foo is not a function ' // ' This'll run! ' varfunction/ function expression assigned to local variable ' foo ' alert ("This won ' t run!" ); } function // function declaration, given the name ' bar ' Alert ("This would run!" ); }} Test ();
In this case, only the body of the function declaration has been suspended at the top. The name "foo" has been hoisted, but the body is abandoned and assigned during the execution.
This covers the basics of cranes and is not as complex or confusing as it seems. Of course, this is JavaScript, and in some special cases there is a bit of complexity.
Name resolution Order
The most important special case to keep in mind is the name resolution order. Keep in mind that there are four ways to enter a given range of names. The order that I listed above is the order in which they are resolved. In general, if a name is already defined, it will not be overwritten by another property of the same names. This means that the function declaration takes precedence over the variable declaration. This does not mean that the assignment of the name will not work, except that the Declarations section will be ignored. There are some exceptions:
The built-in name parameter behaves strangely. It appears to be declared after the formal parameter, but before the function declaration. This means that the formal parameter with the name parameter will take precedence over the built-in, even if undefined. This is a bad feature. Do not use the name parameter as a formal parameter.
Attempting to use the name as an identifier anywhere will result in a syntaxerror. This is a good feature.
If more than one form parameter has the same name, one of the most recent parameters in the list will take precedence, even if it is undefined.
Named function expressions
You can name a function that is defined in a function expression, and its syntax is similar to a function declaration. This is not a function declaration, and the name has not been introduced into the range, and the name has not been raised. Here are some code to illustrate what I mean:
foo(); // TypeError "foo is not a function"bar(); // validbaz(); // TypeError "baz is not a function"spam(); // ReferenceError "spam is not defined"var foo = function () {}; // anonymous function expression (‘foo‘ gets hoisted)function bar() {}; // function declaration (‘bar‘ and the function body get hoisted)var baz = function spam() {}; // named function expression (only ‘baz‘ gets hoisted)foo(); // validbar(); // validbaz(); // validspam(); // ReferenceError "spam is not defined"
How to apply in code
I strongly recommend that you have only one var statement per range, which is at the top. If you force yourself to do this, you will never have a lifting disorder.
If you have done all of this, your code should look like this:
/*jslint onevar: true [...] */function foo(a, b, c) { var x = 1, bar, baz = "something";}
标准是什么呢?
I found that direct consultation with the ECMAScript Standard (PDF) is usually useful to understand how these things work. The following is a description of the variable Declaration and scope (Section 12th. 2.2 In the old version):
If a variable statement occurs within a functiondeclaration, the variable is defined in the function using the local scope of the function, as described in section 10th. 1.3. Otherwise, use the Property property {Dontdelete} to define the global scope (that is, they are created as members of the global object, as described in section 10th. 1.3). Creates a variable when the execution scope is entered. The block does not define a new execution scope. Only program and function declarations can produce a new scope. Variables are initialized to undefined when they are created. When Variablestatement is executed, a variable with Initialiser is assigned to its assignmentexpression value instead of when the variable is created.
I hope this article reveals one of the most common puzzles for JavaScript programmers. I try to be as thorough as possible to avoid causing more confusion. If I have any errors or omissions, please notify me.
Scope of JavaScript