闭包如何捕获状态
了解闭包的词法环境。
闭包如何捕获状态 是 CoddyKit 上的免费 JavaScript Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 JavaScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 JavaScript Academy 课程共包含 4 节课。
什么是闭包
闭包是一个函数,它与定义该函数的作用域中的变量引用绑定在一起。即使外部函数已经返回,内部函数仍然会“记住”这些变量。
function outer() {
const message = "hello";
function inner() {
console.log(message); // sees outer variable
}
inner();
}
outer(); // "hello"返回内部函数
当您返回内部函数时,闭包的作用就显现出来了。即使 outer 已经执行完毕,它仍然可以访问外部变量。
function outer() {
const message = "remembered";
return function () {
console.log(message);
};
}
const fn = outer();
fn(); // "remembered"词法作用域
闭包之所以有效,是因为词法作用域:函数可以访问源代码中物理上包围它的作用域里定义的变量,而不是访问它时所在作用域里的变量。
const x = 10;
function show() {
console.log(x); // resolves where defined
}
function run() {
const x = 99;
show(); // still logs 10, not 99
}
run();按引用捕获
闭包捕获的是变量本身,而不是变量值的快照。如果变量之后发生变化,闭包看到的也会是新值。
function make() {
let count = 0;
const get = () => count;
count = 5; // changed after get defined
return get;
}
console.log(make()()); // 5每次调用都会创建新作用域
外部函数每次调用都会创建一组全新的变量。不同调用产生的闭包不会共享状态。
function counter() {
let n = 0;
return () => ++n;
}
const a = counter();
const b = counter();
console.log(a(), a()); // 1 2
console.log(b()); // 1 (independent)围绕参数的闭包
函数参数也会被捕获。这让您可以从通用函数构建专用函数。
function adder(x) {
return (y) => x + y;
}
const add5 = adder(5);
console.log(add5(3)); // 8
console.log(add5(10)); // 15经典循环陷阱
一个著名的错误:在循环中使用 var 会让所有闭包共享同一个变量。等到它们运行时,循环已经结束,因此它们看到的都是最终值。
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i);
}
console.log(fns[0](), fns[1](), fns[2]()); // 3 3 3使用 let 修复问题
let 会为每次迭代创建新的绑定,因此每个闭包都会捕获自己的 i。这是现代的修复方式。
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i);
}
console.log(fns[0](), fns[1](), fns[2]()); // 0 1 2闭包让内存保持活动状态
只要闭包存在,它捕获的变量就无法被垃圾回收。这通常没有问题,但如果闭包的持有时间超过所需时间,就可能造成内存泄漏。
function make() {
const big = new Array(3).fill("data");
return () => big.length;
}
const f = make();
console.log(f()); // 3 - big stays in memory via f闭包无处不在
回调、事件处理程序、setTimeout、数组方法和承诺都依靠闭包来记住上下文。您一直在使用它们,只是经常没有察觉。
function greet(name) {
return () => console.log("Hi " + name);
}
[greet("Ada"), greet("Bob")].forEach((fn) => fn());
// Hi Ada
// Hi Bob检查捕获的状态
来自同一次调用的多个闭包共享同一组捕获变量,因此一个闭包可以读取另一个闭包写入的内容。
function pair() {
let value = 0;
return {
set: (v) => { value = v; },
get: () => value
};
}
const p = pair();
p.set(42);
console.log(p.get()); // 42快速检查
闭包如何捕获状态。
回顾
闭包是一个函数,加上对其定义作用域的引用。得益于词法作用域,即使外部函数返回后,它仍能记住外部变量,并按引用而不是按值捕获这些变量。每次调用外部函数都会创建一个独立的作用域;请使用 let 来避免循环陷阱。闭包会让捕获的内存保持活动状态,并为各处的回调提供支持。
常见问题解答
「闭包如何捕获状态」课时是免费的吗?
是的 — 「闭包如何捕获状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 JavaScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 JavaScript Academy 课程共包含 4 节课。
「闭包如何捕获状态」这节课中我会学到什么?
了解闭包的词法环境。 你通过在浏览器中直接运行的动手代码来练习 JavaScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 JavaScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 JavaScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「闭包如何捕获状态」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 JavaScript Academy 课中编写并运行代码吗?
能。每节 JavaScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 闭包如何捕获状态
- 使用闭包创建私有变量
- 模块模式(IIFE)
- 工厂函数