五句话搞定JavaScript作用域

Author Avatar
chxpu 6月 22, 2018
  • 在其它设备中阅读本文章

五句话搞定JavaScript作用域(转)

原文:http://www.cnblogs.com/wupeiqi/p/5649402.html

此文针对ES,不涉及ES2015(ES6)

一、“JavaScript中无块级作用域”

  • 大括号不是一个作用域。
    1
    2
    3
    4
    5
    6
    function Main() {
    if(true) {
    var name = 'JAMES';
    }
    console.log(name); // JAMES
    }

补充:标题之所以添加了引号是因为ES6中引入了let关键字 用于指定变量属于块级作用域

二、JavaScript采用函数作用域

  • 在JavaScript中每个函数作为一个作用域,在外部无法访问内部作用域中的变量。
    1
    2
    3
    4
    5
    function Main() {
    var innerValue = 'ABC';
    }
    Main();
    console.log(innerValue); // Uncaught ReferenceError: innerValue is not defined

三、 JavaScript的作用域链

  • 由于JavaScript中的每个函数作为一个作用域,如果出现函数嵌套函数,则就会出现作用域链。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    xo = 'five';
    function Func() {
    var xo = 'six';
    function innerFunc() {
    var xo = 'seven';
    console.log(xo);
    }
    innerFunc();
    }
    Func(); // seven

如上述代码则出现三个作用域组成的作用域链,如果出现作用域链后,那么寻找变量的时候就会出现顺序,对于上述示例:当执行console.log(xo)时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。

四、JavaScript的作用域链执行前已经创建

  • JavaScript的作用域在执行前已经创建,日后再去执行时只需要按照作用域链去寻找即可。
  • 示例一
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    xo = 'five';
    function Func() {
    var xo = 'six';
    function innerFunc() {
    console.log(xo);
    }
    return innerFunc;
    }
    var ret = Func();
    ret(); // six

上述代码,在函数被调用之前作用域链已经存在:
全局作用域 -> Func()函数作用域 -> inneFunc()函数作用域。
当执行ret();时,由于其代指的是innerFunc函数,此函数的作用域链在执行之前就被定义为:全局作用域 -> Func()函数作用域 -> inneFunc()函数作用域,所以,在执行ret();时,会根据已经存在的作用域链去寻找变量。

  • 示例二
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    xo = 'five';
    function Func() {
    var xo = 'six';
    function innerFunc() {
    console.log(xo);
    }
    xo = 'seven';
    return innerFunc;
    }
    var ret = Func();
    ret(); // seven

上述代码和示例一的目的相同,也是强调在函数调用之前作用域链已经存在:
全局作用域 -> Func()函数作用域 -> inneFunc()函数作用域

  • 示例三
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    xo = 'five';
    function Bar() {
    console.log(xo);
    }
    function Func() {
    var xo = 'six';
    return Bar;
    }
    var ret = Func();
    ret(); //five

上述代码中,在函数被执行之前就已经创建了两条作用域链:
全局作用域 -> Bar()函数 ;全局作用域 -> Func()函数;当执行ret(),ret执行Bar函数,而Bar的作用域链已经存在,就会根据Bar函数的作用域链去寻找。

五、声明提前

  • 在JavaScript中如果不创建变量,直接去使用则报错:

    1
    2
    console.log(xxoo);
    // Uncaught ReferenceError: xxoo is not defined
  • 如果创建变量而不赋值,则为undefined:

    1
    2
    3
    var xxoo;
    console.log(xxoo);
    // undefined
  • 在函数内如果这么写:

    1
    2
    3
    4
    5
    6
    7
    8
    function Foo() {
    console.log(xo);
    var xo = 'seven';
    console.log(xo);
    }
    Foo();
    // undefined
    // seven

上述代码,不报错而是输出undefined,其原因是:JavaScript的函数在执行之前,会将其中的变量全部声明,而不赋值。所以,相当于上述示例三中,函数在“预编译”时,已经执行了var xo;所以上述的代码输出是undefined。