最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 闭包原理以及使用场景

    正文概述 掘金(广州蓝景实训部)   2020-11-24   596

    前端面试,必问闭包

    闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包。面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上。

    讲解闭包时,我们先来看个例子

    var n = 999;
    function f1() {
    console.log(n);
    }
    f1(); // 999
    

    上面代码中,函数f1可以读取全局变量n。但是下面例子,函数外部无法读取函数内部声明的变量。

    function f1() {
    var n = 999;
    }
    console.log(n);
    // Uncaught ReferenceError: n is not definedCopy to clipboardErrorCopied
    

    上面代码中,函数f1内部声明的变量n,函数外是无法读取的.

    如果有时需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

    function f1() {
    var n = 999;
    function f2() {
      console.log(n); // 999
     }
    }Copy to clipboardErrorCopied
    

    上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

    闭包是什么

    闭包是一种特殊的对象。

    它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。

    当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

    在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。

    我们可以对上面代码进行如下修改:

    function f1(){
        var a = 999;
        function f2(){
            console.log(a);
        }
        return f2; // f1返回了f2的引用
    }
    var result = f1(); // result就是f2函数了
    result();  // 执行result,全局作用域下没有a的定义,
               //但是函数闭包,能够把定义函数的时候的作用域一起记住,输出999
    

    上面的例子,首先有执行上下文f1,在f1中定义了函数f2,而通过对外返回f2的方式让f2得以执行。当f2执行时,访问了f1内部的变量a。因此这个时候闭包产生

    闭包就是函数f1

    闭包原理以及使用场景

    在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。

    JavaScript拥有自动的垃圾回收机制,关于垃圾回收机制,有一个重要的行为,那就是,当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。

    而我们知道,函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。

    我们再来看个例子:

    var fn = null;
    function foo() {
        var a = 2;
        function innnerFoo() {
            console.log(a);
        }
        fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
    }
    
    function bar() {
        fn(); // 此处的保留的innerFoo的引用
    }
    
    foo();
    bar(); // 2
    

    在上面的例子中,foo()执行完毕之后,按照常理,其执行环境生命周期会结束,所占内存被垃圾收集器释放。但是通过fn = innerFoo,函数innerFoo的引用被保留了下来,复制给了全局变量fn。这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。

    这样,我们就可以称foo为闭包。

    所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。

    不过读者朋友们需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。

    对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。

    var fn = null;
    function foo() {
        var a = 2;
        function innnerFoo() {
            console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误
            console.log(a);
        }
        fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
    }
    
    function bar() {
        var c = 100;
        fn(); // 此处的保留的innerFoo的引用
    }
    
    foo();
    bar();
    

    闭包形成的条件

    • 函数嵌套
    • 内部函数引用外部函数的局部变量

    闭包的应用场景

    初级前端了解以上闭包面试即可,以下内容属于中高级前端理解范围。大家可以暂时跳过,等学有一定基础积累,再回头看看。

    除了面试,在实践中,闭包有两个非常重要的应用场景。分别是模块化与柯里化。

    柯里化

    模块化

    在我看来,模块是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。

    (function () {
        var a = 10;
        var b = 20;
    
        function add(num1, num2) {
            var num1 = !!num1 ? num1 : a;
            var num2 = !!num2 ? num2 : b;
    
            return num1 + num2;
        }
    
        window.add = add;
    })();
    
    add(10, 20);Copy to clipboardErrorCopied
    

    在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。

    闭包原理以及使用场景

    为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。

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

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

    起源地下载网 » 闭包原理以及使用场景

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元