likes
comments
collection
share

JS学习——作用域解析

作者站长头像
站长
· 阅读数 26

前言

作用域大家都不陌生,正所谓面试造火箭,现实拧螺丝,面试时我们会碰到各种类型的题,这些题跟作用域的特性息息相关,这种体现理论的题最容易犯错,往往看了解析会恍然大悟。本节我们就来学习一下作用域的特征。

作用域特征

作用域包含几个特征,掌握了它们,相关面试题也就很简单了:

  • 块级作用域。块级作用域简单来说就是{}里面的区域,es6中的let与const声明的变量就是块级作用域,块级作用域的变量只会在块级里面生效。
   function a(){
      let b = 1
    }
    console.log(b) //b is not defined

b是块级作用域,在块级外就是未定义。除了let、const声明出来的变量,在js中只有函数才有块级作用域。上述例子中我们用let定义了b变量,如果换成var呢?

    function a(){
      var b = 1
    }
    console.log(b) //b is not defined

依旧是未定义的,因为b是在a函数里面的定义的,它就是块级作用域。除此之外在其他地方如果用var它就是全局的。

   if(true){
      var a = 0
    }
    console.log(a) //0

这时候把var换成let或const,那么a肯定就是未定义的,因为这两个声明出来的变量是块级的。三者之间的区别我们要牢牢掌握的。

  • 作用域链。函数中的变量值如果当前函数未定义,会去上一层函数中去寻找,直至找到或者到script全局,这就是作用域链,作用链讲究就近原则,并且只有从内往外找,外部不能访问内部的变量
    function a(){
      var c = 1
      function b(){
        var d = 2
        console.log(c) // 1
      }
      b()
      console.log(d) // d is not defined
    }
    a()
  • 全局变量。如果一个变量直接赋值,没有声明那么它就是全局变量,有着全局作用域,全局变量就是在window上面的。
   (function () {
        var a = b = 1;
      })();
      // console.lg(a); // a is not defined
      console.log(b); // 1

b是直接赋值的,所以b是全局变量,相当于window.b,而a是在函数中定义的,所以a是块级作用域,在函数外打印不出,b却可以。

  • 变量提升。用var声明的变量,会把声明放到最顶层,这种机制被称为变量提升,注意let、const是没有变量提升的。
      console.log(a) // undefined
      var a = 1
      console.log(b) //Cannot access 'b' before initialization
      let b = 2

a用var声明的,var就会放到前面,所以打印undefined而没有报错,但是let就会直接报错。

实例

熟悉以上特性,碰到相关面试题做出来就不在话下,下面我们实战解析,看下具体的,面试题:

  • 第一道:
   function a() {
        var b = 1;
        function c() {
          console.log(b);
          var b = 2;
          console.log(b);
        }
        c();
        console.log(b);
      }
      a();

打印出来的依次是undefined、2、1。这道题考察了变量提升、作用域链,在c函数中变量b进行了声明提升,所以第一次打印b为undefined,后面赋值为2,而a函数中,有声明的b,根据作用域链打印出来为1。

  • 第二道:
   function a(b) {
        console.log(b);
        function b() {}
        console.log(b);
      }
    a(2);

这道题打印出来的是两个b函数,该题考查了变量的优先级,我们在函数中声明了b函数,声明函数提升,所以函数中打印的都是b函数。下面我们将b赋值为函数又会打印什么呢?

   function a(b) {
        console.log(b);
        var b = function () {};
        console.log(b);
      }
      a(2);

答案是2,赋值的函数,注意这次我们是将b进行赋值,而不是直接声明b函数,那为什么第一个不是undefined呢?因为第一个b表示了形参,当我们直接a(2),此时b就是实参的值。通过这两个例子我们知道变量是有优先级,具体的优先级为: 变量声明>函数声明>参数>变量提升 在函数中碰到相同的变量,我们要根据优先级知道它表示的是哪个值。

总结

作用域考查的就是我们的基础,面试中碰到了作用域相关的题,我们可以按照以下步骤去分析:

  1. 变量是否为全局变量、是否在函数内声明。

  2. 找到变量作用域链。

  3. 考虑变量表示的优先级。 通过以上分析,大概率就能得到正确的答案啦。