zoukankan      html  css  js  c++  java
  • 作用域和作用域链浅解析

    作用域:

    变量所在的上下文,指的是变量在哪些地方可以访问

    对于JavaScript来说有全局作用域但是没有块级作用域,在ES6中引入了关键字let可以生成块作用域.见以下代码:

    var value = true
    if (value) {
      var age = 18
      console.log(`我今年${age}岁了`)
    }
    console.log (`我也是${age}岁了哦`)
    
    //输出  "我今年18岁了"   "我也是18岁了哦"
    // 可见在if()中不存在块级作用域
    

    在这里if()里的变量具有全局作用域,全局皆可使用

    var value = true
    if (value) {
      let age = 18
      console.log(`我今年${age}岁了`)
    }
    console.log (`我也是${age}岁了哦`)
    
    /*输出
      "我今年18岁了"
      "error"
      "ReferenceError: age is not defined*/
    

    在这里使用关键字let 使 if () 块里的变量age产生了块级作用域,使得它只在这个块里生效.

    JS中有函数作用域,指的是作用域在函数内部。这里一共说了三种作用域,其实可以说是两种:一种是全局作用域,而是局部作用域(函数作用域、块级作用域),块级作用域概念又包括了函数作用域。


    简要说下几个作用域声明方式:
    1. 全局作用域:在所有函数外部使用var语句声明变量或者在声明变量时忽略var则会隐式转化为全局变量
    2. 函数作用域: 需要在函数内部使用var 声明变量才行
    3. 块级作用域: 在变量名前添加let语句声明(ES6)

    作用域链

    var a = "你好,我是a";
    function scopeChain(a) {
      var b =1;
      function inScope(a) {
         var c = "蚂蚁"
         console.log(`大象爱${c}`)
         console.log(`我是最内层的函数,这里也可以使用a: ${a}`)
      }
      console.log(`能使用a吗?${a}`)
      inScope(a)
    }
    scopeChain(a)
    

    这里a是全局作用域下的变量,b是函数scopeChain()作用域下的变量,而c是函数scopeChain()里的inScope()函数作用域下的变量。

    作用域链的前端始终是当前环境作用域下变量对象,逐层往外作用域链接,最后端是全局变量环境下的变量,这些变量时链接在一起,在解析一个变量时从链前端往后端搜索(从内不找外部找),但是有一点值得注意:每个变量的作用域总是从自身声明的作用域往外找,而不是调用它的地方

    var a = 1
    function fn1(){
      function fn3(){
        var a = 4
        fn2()
      }
      var a = 2
      return fn3
    }
    function fn2(){
      console.log(a)
    }
    var fn = fn1()
    fn()
    
     //输出1
    

    这里就是当fn1() 执行后调用 fn2() 时,发现fn3()作用域下没有,也就是作用域链前端没有,往外找一层也就是fn1()作用域下进行查找,作用域也就往后端前进了一步,发现还是没有,继续往外层作用域查找找到了全局作用域,也就是作用域链的最后端,找到了后调用它。

    但是对于fn2()来说它需要调用a这个变量,这里也就出现了误区:在fn3()里有变量a,那么是用的是这个变量a吗?

    • 但其实fn2()是发现不了这个变量a的,因为fn2()声明的地方并不在fn3()里,同理fn1()也不是fn2()声明的地方,所以对于fn2()来说它只发现了全局下的 var a =1 所以调用它并输出a时也就等于1.

    var a = 1
    function fn1(){
      function fn2(){
        console.log(a)
      }
      function fn3(){
        var a = 4
        fn2()
      }
      var a = 2
      return fn3
    }
    var fn = fn1()
    fn()
    
    //输出多少?
    
    • 由以上的论述可以分析出这段代码,当fn1()被调用时也就是return fn3 ,也就调用了fn3(),然后fn3()里又是调用fn2(),而fn() 是在fn1()里声明的,自然也就使用了fn1()里的变量a,又因为a变量在调用前声明并赋值了,故此输出为2

    var a = 1
    function fn1(){
    
      function fn3(){
        function fn2(){
          console.log(a)
        }
        var a
    
        fn2()
        a = 4
      }
      var a = 2
      return fn3
    }
    var fn = fn1()
    fn()
    
    //输出多少?
    

    分析这段代码,发现与上面代码不同之处在于先声明了 ``` var a ``后没有里脊赋值,在调用了fn2()后再进行的赋值,那么这里应该是多少呢?

    • 这里也就牵涉到了声明前置,对于fn3()下,当声明 var a时,也就是执行到了fn3()代码前,函数声明和变量声明会提前至代码前端,所以这里声明并没有影响到输出值得改变,但是赋值操作是按照程序顺序执行的,当调用前,a只声明没有赋值,则会输出undefined。 而具体变量查找是符合作用域链的顺序来的.

    总结如下:

    1. 函数在执行的过程中,先从自己内部找变量
    2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
    3. 注意找的是变量的当前的状态

    个人学习备忘,如有谬误,欢迎指正。

    (∩_∩)-----代码改变生活。
  • 相关阅读:
    MIne FirstBlog
    P6563 [SBCOI2020]一直在你身旁
    P6563 [SBCOI2020]一直在你身旁
    T122085 [SBCOI2020]时光的流逝
    LC 918. Maximum Sum Circular Subarray
    1026 Table Tennis
    LC 1442. Count Triplets That Can Form Two Arrays of Equal XOR
    LC 1316. Distinct Echo Substrings
    LC 493. Reverse Pairs
    1029 Median (二分)
  • 原文地址:https://www.cnblogs.com/daixixi/p/9394515.html
Copyright © 2011-2022 走看看