• JavaScript:浅谈闭包及其回收原则


    前言

    在JavaScript这门语言中,有一个非常重要但又难以掌握,一个近乎神话的概念,闭包。如果你对词法作用域有一定的理解,那么闭包的概念几乎是不言自明了。
    回忆我写了这么久的JavaScript代码却完全不理解闭包是什么,直到最近读了《浏览器原理》和《你不知道的js》才对闭包有一个比较清晰的认识。看完我的这篇文章,如果你能掌握闭包,必将功力大增。

    理解闭包

    定义:在JavaScript中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数
    已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们把这些变量的集合成为闭包。比如外部函数是foo,那么这些变量的集合就称为foo函数的闭包。
    (不理解定义没关系,先往后看)
    我们先看一段代码:

     function foo() {
                var a = 2
    
                function bar() {
                    console.log(a) // 2
                }
                bar()
            }
            foo() // 2
    

    这是个简单的函数嵌套的例子,基于词法作用域的查找规则,函数bar()可以访问外部作用域中的变量a。
    从技术上来讲,这也许是一个闭包,但如果根据定义来说,它并不是闭包(因为bar函数是在foo()函数内部执行的)。最准确的解释应该是bar()对a的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。
    从纯学术的角度来讲,在上面的代码中,函数bar()具有一个涵盖foo()作用域的闭包。
    但是通过这种方式定义定义的闭包并不能直接进行观察,也无法知道这个闭包是如何工作的。
    再看下面这段代码:

    function foo() {
                var a = 2
    
                function bar() {
                    console.log(a) // 2
                }
                return bar
            }
            var baz = foo()
            baz()
    

    在这段代码中,我们将bar()函数本身当作返回值,在foo()执行后,其返回值赋值给变量baz(),实际上只通过不同的标识符引用调用了内部的函数bar()。
    bar显然能够正常被执行,但是它是在自己定义的词法作用域以外的地方执行的,在foo()执行以后,通常期待foo()的整个内部作用域都被销毁(Js引擎的垃圾回收机制)
    但是因为闭包的存在,事实上内部作用域依然存在,因此没有被回收。谁在使用这个内部作用域?原来是bar()本身在使用。现在再回头看闭包的定义,是不是就很明确了。
    我们也可以通过“开发者工具”来看看闭包的情况,复制上段代码->在编辑器里面运行->F12->source,给console.log(a)打断点->刷新会看到:


    右侧便是内存中闭包的情况。

    闭包的回收

    现在我们知道闭包是什么了。那么如果反复的产生闭包,就有可能会造成内存泄漏,那么了解闭包是如何被回收非常重要。
    1.如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但是如果闭包以后不再使用了,就会造成内存泄漏。
    2.如果引用闭包的函数是一个局部变量,等函数销毁以后,在下次JavaScript引擎执行垃圾回收时,判断闭包这块内容如果已经不再被使用了,那么JavaScript引擎的垃圾回收器就会回收这块内存。
    所以,在使用闭包的时候,你要尽量注意一个原则:如果该闭包会一直使用,那么它可以作为全局变量而存在,但如果使用频率不高,而且占用内存又比较大的话,那就尽量让他成为一个局部变量。
    关于JavaScript垃圾回收机制,后面我会专门出一篇博客来详细的讲解的。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    extjs store 设置额外参数刷新数据
    springsecurity oauth2sso 客户端单点登陆
    extjs store定义 通过ajax访问json数据
    maven deploy异常
    spring security authenticated与fullyAuthenticated的区别
    extjs 部署时动态切换上下文路径
    npm install异常error code EPERM
    包子问题 (背包,找规律?)
    结账问题 (一点点的数学知识)(蓝桥杯)
    倍数问题 (倍数》余数 // 思路的转换问题)(蓝桥杯)
  • 原文地址:https://www.cnblogs.com/XF-eng/p/14202047.html
Copyright © 2020-2023  润新知