深入谈谈JavaScript的作用域及作用域链

348 阅读3分钟

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

前言

作用域和作用域链在前端里是比较简单的概念,但往往因为简单而被人忽视,涉及到作用域链产生的bug很容易让人摸不着头脑。其实再简单的知识点也有复杂的地方,就像再复杂的概念也有易于理解的点一样。今天大冰块从 作用域变量范围作用域链 这三个方面好好谈一谈JavaScript的作用域及作用域链,希望各位同学看完之后能有所收获。

作用域

概念:变量声明后,能够在哪个范围内起作用。

块级作用域

块级作用域: 由花括号组成代码结构的范围 {} ,在花括号里面声明的变量在外部无法访问。 其实JavaScript是不支持块级作用域的,那么为什么大冰块还要说一下这个呢?多了解一点总是没坏处的,比如面试的时候,别人没说这个,但是你把这个点一提,是不是水平立马就提升了?(手动滑稽)

  if(true){
      var num = 100
  }
  console.log(num) // 100   js可以获取到{}内声明的变量


  for(var i = 0; i < 10; i++){
      var str = "能访问到我吗"
  }
  console.log(str) // 能访问到我吗    js可以获取到{}内声明的变量

词法作用域

词法作用域: 也叫函数作用域,因为在js里面,只有函数才能形成作用域。也叫静态作用域,因为它的作用域是指在词法分析阶段就确定了,不会改变。动态作用域是在运行时根据程序的流程信息来动态确定的,而不是在写代码时进行静态确定的。而词法作用域是在写代码或者定义时确定的,而动态作用域是在运行时确定的(this也是!)。词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。

  function fn(){
      var num = 100
  }
  console.log(num) // num is not defined

变量范围

根据作用域把变量分为3类

全局变量

在函数外部声明的变量就是全局变量,在任意位置都能够访问到

  var num = 100
  function fn(){
    console.log(num) // 100
  }
  console.log(num) // 100

局部变量

在函数内部声明的变量就是局部变量,只能在函数内部使用。

  function fn(){
      var num = 100
  }
  console.log(num) // num is not defined

自由变量

对于一个函数来说,如果这个变量不是在这个函数内部声明的,但是却使用了这个变量,对于这个函数来说,这个变量就是自由变量

  var a = 100
  function fn(){
      var b = 200
      console.log(a) // 100,对fn函数来说,a是自由变量
      console.log(b) // 200
  }
  fn()

作用域和变量的关系

函数的作用域在函数定义的时候就已经确定下来了,函数定义的时候就已经确定下来了,函数定义的时候就已经确定下来了,重要事情说三遍。也就是说,函数作用域和函数在哪调用是没有关系的,以及函数如何被调用都是没有关系的。和this不同,this是在函数被调用的时候才确定,函数是如何被调用的。 函数作用域的查找是如何查找到呢?

1. 如果变量是在函数内部声明的,直接在当前作用域内就查找到了
2. 如果变量是自由变量,去创建这个函数的作用域中去查找

好了,说了这么多,出一道题考考你,请看下面的题目:

  var num = 123
  function f1() {
    console.log(num)
  }

  function f2(){
    var num = 456
    f1()
  }
  f2() // 这里会打印啥?

各位同学,请把你的答案贴在评论区。

作用域链

作用域链:函数能够形成作用域,如果函数被嵌套在另一个函数中,嵌套的函数也有自己的作用域,从这个函数的作用域往外形成的一条链, 这个链叫做作用域链。例如:

function outer(){
    function inner(){
        function fn(){
        }
    }
}

上面代码中: 函数outer的作用域链: outer作用域 ==> 全局作用域 函数inner的作用域链: inner作用域 ==> outer作用域 ==> 全局作用域 fn的作用域链: fn作用域 ==> inner作用域 ==> outer作用域 ==> 全局作用域

作用域链: 变量搜索规则:

1. 首先在当前作用域内查找是否有声明该变量,如果有,直接返回。
2. 如果没有,向上一级作用域查找,,如果有,直接返回。
3. 如果没有,沿着作用域链进行查找,直到全局作用域,如果有,直接返回。
4. 如果一直找到最后还是没有,就会报错not defined
5. 在哪一层找到了该变量的声明,直接返回结果,就不继续查找。

好了,下面再出一道题考考大家:

  var num = 1000
  function outer(){
      var num = 999
      function inner(){
          var num = 888
          function fn(){
              var num = 777
              console.log(num)
          }
          fn()
      }
      inner()
  }
  outer()

各位同学,请把再次把你的答案贴在评论区。

后记

在平时写代码的过程中,如果作用域和作用域链如果理解的不透彻,很容易产生bug。相信通过大冰块从作用域,变量范围,作用域链这几个方面的详细解读,你一定对作用域和作用域链有了更透彻的理解。如果本篇文章由帮助到你,希望能点赞支持一下,不枉我熬夜淦完这篇文章。如果有错误也欢迎指出交流。感谢阅读~

PS: 今天是参加掘金更文挑战的第9天啦,没有存稿的我,今天也是拼着熬夜更了这篇文章,一起加油吧~

更文挑战的文章目录如下: