ECMAScript闭包
闭包是函数与其词法环境绑定后形成的可访问组合。
#concept
#growing
#javascript
#ecmascript
[!info] related notes
- 所属 MOC: ECMAScript MOC, ecmascript
- 前置概念: ECMAScript 函数, ecmascript-execution-context
- 相关概念: this 关键字, 柯里化
- 应用关系: 柯里化, 防抖与节流
- 常见场景: ecmascript异步
ECMAScript闭包
闭包可以先记成一句话:函数把定义时可访问的词法环境一起带走了。
最短定义
- 闭包不是单独一种语法,而是函数和其外层词法环境的组合
- 只要函数在别处执行时,仍然能访问定义位置的变量,就已经进入闭包语境
- 最常见形式是函数返回函数,但回调、事件处理器、模块工厂也都会产生闭包
它为什么成立
- ECMAScript 使用词法作用域,变量可访问性由定义位置决定
- 函数是一等对象,可以被返回、传递、保存
- 只要内部函数还被引用,相关词法环境就可能继续存活
它主要解决什么
- 保留跨调用的私有状态
- 给回调保留创建时的上下文
- 构造函数工厂、模块封装、柯里化等模式都依赖这种能力
最容易误解的点
- 闭包不等于内存泄漏;问题在于不再需要的引用没有释放
- 闭包捕获的是变量绑定,不一定是当时的值快照
- 只背“return 一个内部函数”不够,重点是词法环境没有丢
一个最小例子
function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const next = makeCounter();
next(); // 1
next(); // 2
makeCounter() 已经执行结束,但返回的函数仍然能访问 count,这就是闭包最典型的表现。
这篇和相邻笔记怎么分工
- 看函数定义与调用方式:ECMAScript 函数
- 看词法环境、提升、调用栈:ecmascript-execution-context
- 看异步回调里的实际表现:ecmascript异步
与柯里化的关系
柯里化 经常利用闭包保存已经接收的参数,但“闭包”是语言机制,“柯里化”是建立在函数和闭包之上的函数变换模式。
本页只维护闭包本体;柯里化的定义、实现与部分应用边界统一放在 柯里化。
学习检查
不看正文回答:
- 闭包为什么不是“函数里面再写一个函数”的语法现象?
- 词法环境与函数定义位置如何决定闭包能访问哪些绑定?
- 为什么闭包捕获的是变量绑定,而不一定是创建时的值快照?
- 函数已经离开原始调用栈后,为什么仍可能访问外层变量?
- 闭包为什么可能延长对象生命周期,但又不能等同于内存泄漏?
- 防抖、事件回调、私有状态、柯里化分别怎样利用闭包?
- 给一个循环 + 异步回调案例,闭卷预测输出,并解释捕获绑定发生了什么。