• 闭包浅析



    一、闭包印象

    闭包是JavaScript里绕不开的话题,本文旨在让你理解闭包,阐述闭包的一些特性防止不知不觉掉坑,并在适当的条件下利用好这些特性。
    关于闭包的权威定义,犀牛书上说得很透彻了(其实我看得有些晕),阮一峰老师学习Javascript闭包(Closure)和耗子哥理解Javascript的闭包讲的也深入浅出,我这里参考耗子哥的定义说明我的理解:定义在一个函数内部的函数就可以看做是闭包。来一段代码:

    //代码一
    var a = "gloable variable";
    var F = function(){
        var b = "local variable";
        function N(){   //闭包
            var c = "inner variable";
            return b;   //对父级作用域的引用
        }
        return N;
    }
    
    var local = F();
    local();    //输出:local variable
    

    正常情况下全局作用域是无法访问局部变量b的(作用域链原理),但是利用闭包就可以突破作用域链的限制。在代码一中,可以看成通过 var local = F() 将N变成了一个全局作用域中的函数。犀牛书上的一种解释为这是在不同的上下文环境分别定义(在函数F的作用域定义)和执行(在全局环境执行)函数所发生的微妙现象。

    二、分析闭包

    常说每个函数都可看做是一个闭包,当然没错,可是个人观点觉得这样的解释闭包没有实际意义,也没有说清楚闭包的特性,仅能说明闭包是一种常见现象。
    个人认为,闭包的一个重要特性在于,内部函数在定义时是绑定了其所在的作用域本身的,这种绑定导致函数内部的变量值会随着作用域自身的变化而变化,而非内部函数在被定义或父级变量被返回时变量的值。举个例子:

    function F(param){
        function inner(){   //内部函数被定义
            return param;   //父级变量被返回
        }
        param++;    //在与inner绑定的作用域修改param的值
        return inner;   
    }
    var a = F(123);
    a();    //输出:124
    

    在F中,定义和变量被返回都发生在修改变量之前,可见闭包并不会“记录”父级变量的值,而是由于这种绑定关系而忠实地反应变量实际的值。简单一点说,这里的inner在被调用时才会通过绑定关系去读取param的值,其他时候闭包并不关心param的值。

    三、关于闭包的坑

    既然闭包很常见,若不注意我们很可能误用了他的特性。循环中的闭包就是经常会碰到的坑。

    var F = function(){
        var a = [];
        for(var i = 0; i < 3; i++){
            a[i] =function(){   //这里定义了三个闭包
                return i;
            }
        }   
        return a;
    }
    
    var arr = F();
    arr[0]();
    arr[1]();
    arr[2]();   //均输出:3
    

    如前所述,闭包在被最后调用前都不会去读取i的值,仅仅是保留了对i的引用关系,所以最后被调用时实际读取的都是同一个i值。好吧,如何防止这种不希望的现象出现呢?

    var F = function(){
        var a = [];
        for(var i = 0; i < 3; i++){
            a[i] = function(x){ //即时函数读取i值给x
                return function(){
                    return x;
                }
            }(i);
        }   
        return a;
    }
    
    var arr = F();
    arr[0]();   //输出:0
    arr[1]();   //输出:1
    arr[2]();   //输出:2
    

    通过即时函数我们在每次给数组赋值时读取i的实际值,这样就绕开了闭包的限制。

    四、 闭包的实际应用

    闭包最常用的地方就是setter和getter了。
    假设有一个内部值,我们不能让其暴露在全局环境中以防止其被随意修改,只能通过我们规定的途径修改,当然也可以在这些途径中加入一些验证机制。举个例子:

    var wenzi = (function(){
        var inner = "meta-d"; //内部变量
        var getValue = function(){
            return inner;
        }
        var setValue = function(val){
            if(typeof val == "string"){ //简单验证
                inner = val;
            }
            console.info(inner);
        }
    
        return {
            getValue : getValue,
            setValue : setValue
        }
    })();
    wenzi.getValue();   //输出:meta-d
    wenzi.setValue("test"); //输出:meta-d
    wenzi.getValue()   //输出:test
    

    除此外,闭包可用的地方还有很多,很多时候我们用了闭包但是自己并没有意识到。但万变不离其宗,了解闭包的原理才是正道。还可参考司徒大神的文章javascript的闭包
    行文仓促,如有不妥之处,还请拍砖,轻拍,谢谢!

  • 相关阅读:
    GZS与小公园(DFS)
    II play with GG(思维规律)
    bearBaby loves sleeping(BFS)
    湖南大学新生赛C,G,J题解
    bootstrap 标签页的使用(tab)
    js 循环生成元素,并为元素添加click事件,结果只执行最后一个点击事件
    使用原生js实现一个列表数据展示页面不同的项目状态使整行显示不同颜色。
    【Vue】详解Vue组件系统 目录
    基于TCP与UDP协议的socket通信
    ElementUI三级菜单checkBox全选实现
  • 原文地址:https://www.cnblogs.com/mengda1027/p/4595922.html
Copyright © 2020-2023  润新知