JavaScript authoritative Guide (4): JavaScript scope and Ascending source address: http://dyy.im/4406.html
Do you know what value will be output when the following JavaScript program executes?
var foo = 1;function bar() { if (!foo) { var foo = 10; } alert(foo);}bar();
The answer is "10", is it surprising? Then the following may be a real surprise to you:
var a = 1;function b() { a = 10; return; function a() {}}b();alert(a);
Here the browser will pop up "1". What's going on here? This may seem strange, unknown, confusing, but this is actually a powerful and expressive feature of the language. I don't know if this behavior has a standard name, but I like the term "ascension" (hoisting). This article attempts to reveal the mechanism of this feature, but first let us understand the scope of JavaScript.
Scope (scope) in JavaScript
The easiest place for JavaScript beginners to confuse is scope. In fact, not just beginners. I've met many experienced JavaScript programmers, but I don't fully understand scopes. The reason the scope of JavaScript is so confusing is that it looks much like the C-family language (Class C language). Consider the following C program:
#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 the program is 1,2,1. This is because the C and C family languages have a block-level scope (block-level scope). When the control flow enters a block, such as an if statement, the new variable is declared in the block scope and does not make an impression on the outside scope. This does not apply to JavaScript. Run the following code in the Firebug:
var x = 1;console.log(x); // 1if (true) { var x = 2; console.log(x); // 2}console.log(x); // 2
In this example, Firebug will output 1,2,2. This is because JavaScript has a function-level scope (function-level scope). This is completely different from the C family. Statement blocks, such as the If language, do not create a new scope. Only the function creates a new scope.
Many programmers, like c,c++,c# or Java, do not know this, nor do they want it. Fortunately, because of the flexibility of JavaScript functions, there is a solution. If you have to create a temporary scope inside the function, do the following:
function foo() { var x = 1; if (x) { (function () { var x = 2; // 此处省略一万个字 }()); } // x 仍然是 1.}
This method is actually quite flexible and can be used arbitrarily when you need a temporary scope, not confined to block-level statements. However, I strongly recommend that you take the time to understand and appreciate the scope of JavaScript. It's very powerful and is one of my favorite features in this language. If you know the scope, it will be easier to understand the ascension.
Declaration, name and promotion (hoisting)
In JavaScript, the name (attribute name) in the scope has four basic sources:
Language definition: All scopes By default have the property name this and arguments.
Formal parameter: A function may have formal parameters whose scope is within the entire function body.
function declaration: This form resembles function foo () {}.
Variable declaration: var foo; This form of code. function declarations and variable declarations are always implicitly moved by the JavaScript interpreter to (Ascend) the top containing their scopes. The names of function parameters and language definitions are clearly always present. This means like the following code:
function foo() { bar(); var x = 1; }
is actually interpreted as follows:
function foo() { var x; bar(); x = 1;}
The above procedure occurs regardless of whether the line of code that contains the declaration is 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) { x = 1; } return; y = 1;}
Note that the process of assigning values in a variable declaration is not promoted, only the name of the variable is promoted. But this does not apply to function declarations, and the entire function body is also promoted. But don't forget that there are two ways to declare a function, consider the following JavaScript code:
function test() { foo(); // 类型错误 “foo 不是一个函数” bar(); // “这能运行” var foo = function () { // 将函数表达式赋值给本地变量“foo” alert("this won‘t run!"); } function bar() { // ‘bar‘函数声明,分配“bar”名字 alert("this will run!"); }}test();
In this case, only the function body of the function declaration is lifted to the top. The name "foo" is promoted, but the function body behind it is assigned at the time of execution.
This is all about JavaScript ascension, and it doesn't look complicated and confusing. Of course, this is JavaScript, which can be a bit more complicated in some specific situations.
Name resolution Order
The most important special case to remember is the order in which the names are parsed. Remember that the name in the scope has four sources. The order in which I list them is the order in which they are parsed . In general, if a name has already been defined, it will no longer be rewritten by other attributes with the same name. This means that the function declaration takes precedence over the variable declaration. This does not mean that the process of assigning a value to a name will not work, and simply declaring the process will be ignored. There are a couple of exceptions:
- The built-in variable arguments of the function is rather strange. It appears to be declared after a normal function argument, actually before the function declaration. If the parameter has a parameter named arguments, it will be higher than the built-in priority, even if it is undefined. So do not use arguments as the name of the function parameter.
- Attempting to use this as the identifier will cause a syntax error. This is a good feature.
- If more than one parameter has the same name, the last parameter will take precedence over the previous one, even if it is undefined.
Named function expressions
You can name a function in a function expression, you can't complete a function declaration with such a syntax, 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 () {}; // 匿名函数表达式(“foo”会被提升)function bar() {}; // 函数声明(“bar”和函数体会被提升)var baz = function spam() {}; // 命名函数表达式(仅“baz”会被提升)foo(); // validbar(); // validbaz(); // validspam(); // ReferenceError "spam is not defined"
How to use this knowledge when coding
Now that you understand scope and elevation (hoisting), what should we do when we write JavaScript? The most important thing is to always declare your variables with the VAR expression. I strongly recommend that you use single var mode. If you force yourself to do this, you will never encounter any confusing problems associated with variable promotion. But doing so also makes it difficult for us to keep track of variables that are actually declared in the current scope. I suggest you use jslint and declare the principle to do the actual work, if you do so, your code should look like this:
/*jslint onevar: true [...] */function foo(a, b, c) { var x = 1, bar, baz = "something";}
The explanation given by the standard
I turned over the ECMAScript standard, want to know directly how these things work, found the effect is good. Here I have to say something about variable declarations and scopes (Section 12th. 2.2):
If you declare a variable in a function, these variables are defined in the function scope of the function, as described in 10th. 1.3. Otherwise they are defined in the global scope (that is, they are created as members of the global object, as described in 10th. 1.3), and the variable is created when it enters the execution environment. A block of statements cannot define a new scope. Only one program or function declaration can produce a new scope. When a variable is created, it is initialized to undefined. If a variable declaration statement has an assignment operation, the assignment occurs only when it is executed to the declaration statement, not when it is created.
I hope this article illustrates the most common confusion for JavaScript programmers, and I try to speak as exhaustively as possible to avoid causing more confusion, and if I'm wrong or have a big omission, please let me have it.
Original http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
JavaScript authoritative Guide (4): Scope and elevation of JavaScript