• 前端基础进阶(四):详细图解作用域链与闭包


    https://segmentfault.com/a/1190000012646488  https://yangbo5207.github.io/wutongluo/

    说明:此处只是记录阅读前端基础进阶的理解和总结,如有需要请阅读上面的链接

    一、作用域链

    作用域链是由当前环境和当前环境以上的一系列变量对象组成的,它保证了有权限的变量对象的有序访问。这句话怎么理解,看下下面的例子

    var a = 20;
    
    function test() {
        var b = a + 10;
    
        function innerTest() {
            var c = 10;
            return b + c;
        }
    
        return innerTest();
    }
    
    test();

    VO(global),VO(test), VO(innerTest)分别表示全局上下文,函数test的执行上下文,函数(innerTest)的执行上下文的变量对象,那么函数test的作用域链包括VO(test)、VO(global),而函数innerTest的作用域链则包括以上三个变量对象。

    可以用一个数组来表示作用域链,当前环境的变量对象放在作用域链的前端,末端是全局上下文的变量对象。可以把作用域链理解为以最前端为起点,最末端为终点的单方向通道

    innerTestEC = {
        VO: {...},  // 变量对象
        scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链
    }

    二、闭包

    如果子函数B中访问了父函数A中的变量,则形成闭包。大部分书籍以函数B指代闭包,如闭包B,而谷歌浏览器以函数A指代闭包。

    闭包的存在使得原本应该在函数结束时释放的变量空间保存了下来,供子函数调用的时候使用。

    // demo01
    function foo() {
        var a = 20;
        var b = 30;
    
        function bar() {
            return a + b;
        }
    
        return bar;
    }
    
    var bar = foo();
    bar();

    如上函数bar访问了父函数foo中的变量a,b形成闭包,并且当foo函数执行完时,变量对象a,b并没有被释放,而是保存了下来供函数bar使用,正常情况下如果没有闭包变量对象a,b是应该释放的。

    闭包只产生在只有父子关系的函数中吗,其实不是的,只要函数B访问了其以上的任何一个函数的变量对象,闭包都会产生,看如下例子

     function foo() {
                var a = 20;
                var b = 30;
    
                function bar() {
                    return function dd() {
                        return a + b;
                    }
                }
    
                return bar;
            }
    
            var bar = foo();
            var dd = bar();
            console.log(dd());

    三、练习题

    利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5

    for (var i=1; i<=5; i++) {
        setTimeout( function timer() {
            console.log(i);
        }, i*1000 );
    }

    这个题目需要了解setTimeout的详细用法和闭包的概念,详细答案https://mp.weixin.qq.com/s/XB6OS2b162fV8YQ-TzE_nQ

    for (var i=1; i<=5; i++) {
        setTimeout( (function(i) {
            return function() {
                console.log(i);
            }
        })(i), i*1000 );
    }

    这里记一下为什么会用到自执行函数,用到自执行函数主要是为了保存i的值,利用传参把每次i的值保存在每一个自执行函数的参数中,然后利用闭包访问每次保存的值。需要注意的是setTimeout里面的函数中的i和for循环的i其实是两个不同对象,它是函数的参数保存着i的值,因此也可以像下面这样写

    for (var i = 1; i <= 5; i++) {
                setTimeout((function (a) {
    
                    return function () {
                        console.log(a);
                    }
                })(i), i * 1000);
            }
  • 相关阅读:
    假期学习总结2-14
    假期学习总结2-13
    假期总结2-12
    假期总结2-11
    读人月神话
    冲刺第五天 11.29 THU
    冲刺第四天 11.28 WED
    冲刺第三天 11.27 TUE
    冲刺第二天 11.26 MON
    冲刺第一天 11.23 FRI
  • 原文地址:https://www.cnblogs.com/lidaying5/p/8476365.html
Copyright © 2020-2023  润新知