zoukankan      html  css  js  c++  java
  • 【JS核心概念】作用域与作用域链

    重点:

    • [x] 父级作用域无法访问其子级作用域中的变量和函数,子级作用域可以使用其父级作用域中的变量和函数;
    • [x] 当执行一个函数的时候,如果我们需要查找某个变量的值,那么会去这个函数被定义时所在的作用域链中去查找,一旦找到需要的变量,就会停止向后追溯。

    一、作用域

    简单来说,作用域就是变量和函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。作用域又分为全局作用域、局部作用域以及ES6中的块级作用域。

    • 全局作用域
      • 全局作用域中的变量和函数在任何地方都可以被访问到;
      • 未定义直接赋值的变量为自动变为全局变量;
        var a1 = 1;
        function say1() {
            console.log(a1); // 1,函数内部也可以访问全局变量
            a2 = 2;
            console.log(a2); // 2,a2自动声明为拥有全局作用域,在严格模式下这种写法会报错
        }
        say1();
        console.log(a2); // 2
        
        // 结果为:1 2 2
    
    • 局部作用域
      • 在函数中声明的变量会变为函数的局部变量,只能在函数内部使用,因此函数作用域又称为局部作用域;
      • 每个函数都有自己的作用域;
        function say2(a) {
            console.log(a); // 2
            var b = 1;
            console.log(b); // 1
        }
        say2(2);
        console.log(a); // ReferenceError: a is not defined,函数的参数也是该函数的局部变量
        console.log(b); // ReferenceError: b is not defined,局部变量不能被外部访问
    
    • 块级作用域
      • ES5中没有块级作用域,这导致很多场景都不合理:

        • 内层变量可能会覆盖外层变量
            var a = 1;
            // 函数fn的作用是if代码块内部使用内层的变量a,外部使用外层的变量a。
            // 但是函数fn执行后,输出结果为undefined,原因在于变量提升导致内层的变量a覆盖了外层的变量a。
            function fn() {
                console.log(a);
                if (false) {
                    var a = 2;
                }
            }
            fn(); // undefined
        
        • 用来计数的循环变量泄漏为全局变量
            // 变量i用于控制循环,但是循环结束后,变量i没有被销毁,而是变成了全局变量
            for (var i = 0; i < 5; i++) {
                console.log(i); // 0 1 2 3 4
            }
            console.log(i); // 5
        
      • ES6新增了块级作用域

        • 使用let和const命令声明变量会形成块级作用域
            function fn() {
                let a = 1;
                if (true) {
                    let a = 2;
                }
                console.log(a);
            }
            fn(); // 1
        
            for (let i = 0; i < 5; i++) {
                console.log(i); // 0 1 2 3 4
            }
            console.log(i); // ReferenceError: i is not defined
        
            {
                const c = 'C'; 
                {
                    console.log(c); // C
                }
            }
            console.log(c); // ReferenceError: c is not defined
        

    二、作用域链

    作用域链,顾名思义,可以想象为是由许多个作用域串在一起形成的一个链。那么问题来了,这许多个作用域是从何而来呢?又是如何串在一起形成链的呢?

    • 作用域链的形成
      • 程序执行时,会先创建全局上下文,并将全局上下文的变量对象挂载在作用域链的后端端;
      • 如果全局上下文中调用了一个函数,则创建该函数的执行上下文,并将该函数上下文的变量对象挂载在全局上下文变量对象之上;
      • 如果该函数的内部又调用了一个函数,同样创建该函数的执行上下文,并将其变量对象挂载在外层函数上下文变量对象之上……
      • 以此类推,形成了一个作用域链。
    • 作用域链的特点
      • 作用域链的前端是当前正在执行的代码所处环境的变量对象;
      • 全局上下文的变量对象位于作用域链的后端
    • 查找标识符

    当在某个环境中为了读取或者写入而引入一个标识符时,必须通过搜索来确定该标识符实际代表什么。

    搜索过程从作用域链的前端开始,向后逐级查询与给定名字匹配的标识符,如果找到了,则停止搜索,变量就绪;如果追溯到了全局环境的变量对象还是没有找到,则说明该变量尚未声明。

    三、练习时刻

        var a = 1;
        function fn() {
            console.log('1:' + a);
            function bar() {
                console.log('2:' + a)
            }
            var a = 2;
            bar();
            console.log('3:' + a);
        }
        
        fn();
        
        // 输出结果为:
        // 1:undefined => 使用var定义的变量会在当前作用域提升,且只提升声明,不提升赋值
        // 2:2 => 函数bar定义在函数fn中,因此其作用域为bar->fn->global,bar中没有定义a,则去fn中查找,在fn中找到后返回
        // 3:2 => 这行代码所在的作用域为fn->global,所以会在先在fn中查找a,找到后返回a的值
    
        var a = 1;
        function fn() {
            console.log('1:' + a);
            var a = 2;
            bar();
            console.log('2:' + a);
        }
        function bar() {
            console.log('3:' + a);
        }
        
        fn();
        
        // 输出结果为:
        // 1:undefined => 使用var定义的变量会在当前作用域提升,且只提升声明,不提升赋值
        // 3:1 => 函数bar是定义在全局作用域中的,所以作用域链是bar->global,bar中没有定义a,则去global中查找
        // 2:2 => 这行代码所在的作用域为fn->global,所以会在先在fn中查找a,找到后返回a的值
    
        var a = 1;
        function fn() {
            console.log('1:' + a);
            a = 2;
        }
        a = 3;
        function bar() {
            console.log('2:' + a);
        }
        
        fn();
        bar();
        
        // 输出结果为:
        // 1:3 => ① fn中的a=2是给变量a赋值,而不是声明变量a,因此执行fn时,查找到的变量a是全局作用域中的a;② JS中的代码是顺序执行的,因此执行fn之前已经执行了a=3,此时全局作用域中的a的值为3
        // 2:2 => ① fn中的a=2修改了全局作用域中a的值,因此执行bar时,a的值为2
    
        var a = 1;
        function fn(f) {
            var a = 2;
            return f;
        }
        function bar() {
            console.log(a)
        }
        
        var f1 = fn(bar);
        f1();
        
        // 输出结果为:
        // 1 => 函数中变量的值由函数定义时的所在的作用域链决定
    
        var a = 1;
        function fn(f) {
          var a = 2;
          return function () {
            console.log(a)
          }
        }
        
        var f1 = fn();
        f1();
        
        // 输出结果为:
        // 2 => 函数中变量的值由函数定义时的所在的作用域链决定
    



  • 相关阅读:
    2013.4.15 Particle Swarm Optimization with Skyline Operator for Fast Cloudbased Web Service Composition
    Adaptive service composition in flexible processes
    2013.4.13 DomainSpecific Service Selection for Composite Services
    2013.4.14 Modeling and Algorithms for QoSAware Service Composition in VirtualizationBased Cloud Computing
    2013.5.29 Towards Networkaware Service Composition in the Cloud
    Efficient algorithms for Web services selection with endtoend QoS constraints
    SQL Server中常用的SQL语句
    接口限流自定义注解
    linux服务器生产环境搭建
    MVEL自定义函数重复掉用报错:duplicate function
  • 原文地址:https://www.cnblogs.com/jiafifteen/p/12201388.html
Copyright © 2011-2022 走看看