闭包
约 1053 字大约 4 分钟
2021-03-29
什么闭包,闭包有什么用?
JS高级程序设计中的定义:闭包是指有权访问另一个函数作用域中的变量的函数。
主要作用就是为了获取函数内部的变量和将变量保存起来,使其不被垃圾回收器回收,供后边使用。
创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部,是属于某个作用域内定义的函数,它可以访问这个作用域内的所有变量。
闭包作用域链通常包括三个部分:
- 函数本身作用域。
- 闭包定义时的作用域。
- 全局作用域。
闭包常见用途/使用场景:
- 封装作用域,用闭包来代替全局变量,避免全局变量污染
- 函数外或在其他函数中访问某一函数内部的参数
- 在函数执行之前为要执行的函数提供具体的参数
- 在函数执行之前为函数提供只有函数执行或者引用时才能知道的具体参数
- 为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
- 实现JS模块化
闭包会产生一个很经典的问题:多个子函数的作用域都是同时指向父级,是完全共享的。因为当父级的变量对象被修改时,所有子函数都受到影响。
解决:
- 变量可以通过函数参数的形式传入,避免使用默认的作用域向上查找
- 使用setTimeout包裹,通过第三个参数传入
- 使用块级作用域,让变量成为自己上下文的属性,避免共享。
闭包使用的优缺点?
优点:
- 保护函数内部变量的安全,加强了封装性
- 在内存中维持一个变量
- 设计私有方法和变量
- 可以读取函数内部的变量
缺点:
- 导致内存泄漏,使用不当会造成额外的内存占用
- 可以改变父函数的变量,所以使用时要谨慎
需要使用闭包,但如何避免内存泄漏(Javascript垃圾回收方法)?
标记清除(mark and sweep)
- 这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。
- 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了。
引用计数(reference counting)
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间