javascript 变量作用域(scope)与变量提前(hoisting)

下面两段代码输出什么:

1 var foo = 1;
2 function bar() {
3   if (!foo) {
4     var foo = 10;
5   } // end if
6   alert(foo);
7 } // end bar()
8 
9 bar();  // 10

你可以再这里查看结果:http://jsfiddle.net/pMfNk/

1 var a = 1;
2 function b() {
3   a = 10;
4   return;
5   function a() {}
6 } // end b()
7 
8 b();
9 alert(a); // 1

可以再这里查看结果: http://jsfiddle.net/g8NZ4/

这里牵涉到javascript变量作用域和变量声明提前。

javascript是函数作用域。不是块作用域。也就是说通过函数定义新的作用域。这一特性通常用来定义命名空间、闭包等。

一个变量名有四种方法进入作用域:

  1. 语言定义:所有作用域默认都定义了this和arguments
  2. 参数名称:函数声明时定义的参数列表
  3. 函数声明:通过这种格式声明的函数:function foo() {}
  4. 变量声明:通过这种形式声明的变量: var foo;

规则1: 所有函数声明和变量声明都将被提前到它们作用域的顶端。在这里保存着函数参数和语言定义的变量。也就是说不管声明变量的语句被执行到,它们都会被声明。

 1 function foo() {
 2   bar();
 3   var x = 1;
 4 }
 5 
 6 // 实际上会被这样解析
 7 
 8 function foo() {
 9   var x;
10   bar();
11   x = 1;
12 }
13 
14 // 下面两个函数时一样的效果
15 function foo() {
16   if (false) {
17     var x = 1;
18   }
19   return;
20   var y = 1;
21 }
22 
23 function foo() {
24   var x, y;
25   if (false) {
26     x = 1;
27   }
28   return;
29   y = 1;
30 }

规则2:变量声明部分被提前,但是赋值部分不会提前。当函数声明时需要注意,函数声明有两种方法:变量赋值法和直接声明法。变量赋值与普通变量声明相同。直接声明法将函数体也会提前

 1 function test() {
 2 //  foo(); // typeerror "foo is not a function"
 3   bar(); // this will run
 4   
 5   var foo = function () {
 6     alert("this won't run");
 7   }
 8   function bar() {
 9     alert("this will run");
10   }
11 }
12 
13 test();

到这里查看结果: http://jsfiddle.net/8gUHM/

规则3:名字解析顺序,名字解析顺序将按照前面所列顺序进行,当一个名字已经定义了,它不会被后续同名属性定义。也就意味着函数声明优先级高于变量名声明。声明不会覆盖,但是当函数运行到具体赋值处时仍然会覆盖。当多个参数使用同一个名字时。后出现的将获得更高优先级(赋值顺序导致内容覆盖?)

规则4:命名函数表达式,当将命名的普通函数定义表达式赋值给一个变量时。函数名将不会被声明,函数体也不会被提前,(这里实际上成为了变量声明,函数名也不见了)

 1 function test() {
 2 //  foo(); // typeError "foo is not a function"
 3   bar(); // valid
 4 //  baz(); // typeerror "baz is not a function "
 5 //  spam(); // ReferenceError "spam is not defined"
 6   
 7   var foo = function () {}; // anonymous function expression (foo gets hoisted)
 8   function bar() {}  // function declaration (bar and the function body get hoisted)
 9   var baz = function spam() {}; // named function expression (only baz gets hoisted, spam is never declared)
10   
11   foo(); // valid
12   bar(); // valid
13   baz(); // valid
14 //  spam(); // referenceerror
15 }
16 
17 test();

这里是测试结果: http://jsfiddle.net/3VYTG/

知道这些之后应该怎么做?

  1. 总是使用var声明变量
  2. 将变量声明放到函数顶端
  3. 只用一个var,用JSLint onevar选项检查代码

出处:http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

原文地址:https://www.cnblogs.com/qiudeqing/p/3445232.html