zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Linux下使用ps命令来查看Oracle相关的进程
    虚继承、虚基类
    C++/C小知识点(2)
    C++中的运行中动态类型识别RTTI
    什么应该放在头文件?何为外连接?
    Placement new、operator new、new operator 完全释疑
    二叉搜索树
    Linux C编程学习资料,学习过程
    独立对象(非附属对象)大小
    编程珠玑第一章、第二章
  • 原文地址:https://www.cnblogs.com/qiudeqing/p/3445232.html
Copyright © 2011-2022 走看看