闭包
闭包的定义
如果一个内层函数访问了外层函数作用域里的变量,那么即使外层函数执行完销毁了,内层函数依然能访问到当初那些外层变量,这个 内层函数 + 被它捕获的外层变量 的集合就叫做 闭包。
ECMAScript 与 JavaScript
ES 是 ECMAScript 的缩写,是由国际标准化组织 ECMA 国际 制定的脚本语言的通用规范标准,它不绑定任何具体的宿主环境,只规定脚本语言最核心的语法、特性、规则。
JavaScript 是 ES 规范 最知名、最主流的实现,它在 ES 核心规范的基础上,扩展了不同宿主环境的专属 API:除了完全实现 ES 的所有标准特性外,JS 还会根据运行环境的不同,额外提供环境相关的能力。
两者的关系是 先有 JS 实现,后有 ES 标准,标准是基于 JS 制定的。
闭包的产生条件
闭包的核心是 访问定义时的父作用域变量,如果函数定义在全局作用域,父作用域就是全局,本身就是全局可访问的,不存在闭包的概念。
闭包的存在形式
- 外层函数返回内层函数,同时保留对上层变量的引用
1 | function outer() { |
- 把内层函数传递到外层作用域执行,就会形成闭包
1 | function outer() { |
setTimeout 的回调函数是在 全局执行上下文 中被执行的。
类似的场景还有事件绑定、接口请求回调等,只要回调函数定义在某个作用域内,并且访问了该作用域的变量,就会形成闭包。
- 高阶函数封装创建闭包
1 | // 柯里化求和函数,通过闭包保留已传入的参数 |
防抖、节流、柯里化这类高阶函数,本质都是通过闭包保留状态。
注意事项
被闭包引用的变量不会被垃圾回收机制回收,如果闭包一直存在,这些变量就会一直占着内存。
闭包的核心是 保留外层作用域的变量,如果使用不当会导致内存泄漏。不需要用到闭包的时候及时解除引用,比如事件解绑、把闭包变量赋值为null,让垃圾回收可以正常清理。