• JS基础:闭包和作用域链


    简介

      一个定义在函数内部的函数与包含它的外部函数构成了闭包,内部函数可以访问外部函数的变量,这些变量将一直保存在内存中,直到无法再引用这个内部函数。

    例如:

    var a = 0;
    function outerFun(i) {
        var b = i;
        function innerFun(j) {
            var c = j;
            console.log("全局变量:"+a);
            console.log("外部变量:"+b);
            console.log("内部变量:"+c);
            a++;
            b++;
            c++;
        }
        return innerFun;
    }
    var testFun1 = outerFun(10);
    testFun1(10);//全局变量:0 外部变量:10 内部变量:10
    testFun1(10);//全局变量:1 外部变量:11 内部变量:10
    
    var testFun2 = outerFun(20);
    testFun2(20);//全局变量:2 外部变量:20 内部变量:20 
    testFun2(20);//全局变量:3 外部变量:21 内部变量:20

    结论:

    c 是 innerFun() 的局部变量,每次执行 innerFun() 都会重新赋值;
    b 是 innerFun() 的上一级变量,每次执行 innerFun() 的结果会保存在内存中,是 outerFun() 的局部变量,每次执行 outerFun() 都会重新赋值;
    a 是 innerFun() 的上上一级变量,每次执行 innerFun() 的结果会保存在内存中,是 outerFun() 的上一级变量,每次执行 outerFun() 的结果也会保存在内存中;

      在JavaScript中,作用域只有全局作用域和函数级作用域两种,在下一级作用域中可以访问和修改上一级作用域的变量,在定义函数时,JS会自动维护一个作用域链,当前的局部作用域位于作用域链顶端,并可以依次向上回溯。闭包的实质就是延长了作用域链。

    变量的生存周期

      上面提到,js 中只有全局变量和函数级的局部变量两种,全局变量的生存周期当然是永久的,除非我们主动销毁这个全局变量。而对于在函数内用 var 关键字声明的局部变量来说,当退出函数时,这些局部变量即失去了它们的价值,它们都会随着函数调用的结束而被销毁。

    例如:

    //页面有5个 div,期望点击每个 div 输出它的序号
    for(var i=0; i<5; i++) {
        $('div').eq(i).on('click',function() {
            console.log(i);
        })
    }

    然而,实际上,点击每个 div 输出的都是5,这是因为 i 是全局变量,位于作用域链最顶端,当改变 i 的值的时候,所有引用了它的函数都会受到影响,当循环运行结束后,i 的最终值为5,所以点击每个 div 输出的当然是5了。这时我们可以通过闭包来延长作用域链,为 i 的值保存一个副本,使它们之间不会互相影响。

    例如:

    for(var i=0; i<5; i++) {
        function outerFun() {
            var j = i;
            function innerFun() {
                $('div').eq(j).on('click',function() {
                    console.log(j);
                })
            }
            innerFun();
        }
        outerFun();
    }

    这里的 outerFun() 和 innerFun() 都可以用匿名自执行函数代替,例如:

    for(var i=0; i<5; i++) {
        (function() {
            var j = i;
            (function() {
                $('div').eq(j).on('click',function() {
                    console.log(j);
                })
            })();
        })();
    }
  • 相关阅读:
    C/C++数组名与指针区别深入探索(转)
    mysql 的编译安装
    rpm的问题 ~/.rpmmacros %_rpmlock_path
    GCC中的弱符号与强符号(转)
    关于printf系列函数
    如何修改机器名
    multiple definition of XXXX 的解决
    由无名对象(临时对象)引发的关于“引用”的思考
    关于date中时间字符串的格式
    月薪不同,面试题不同!
  • 原文地址:https://www.cnblogs.com/blog-cxj2017522/p/9001311.html
Copyright © 2020-2023  润新知