闭包形成的三个必要条件是:函数嵌套、内部函数引用外部变量、外部函数返回内部函数;满足后JS保留外部作用域,使内部函数在外部调用时仍可访问并修改原局部变量。

闭包形成的关键在于:函数嵌套 + 内部函数引用外部变量 + 外部函数返回该内部函数。只要满足这三点,JavaScript 就会保留外部函数的作用域,让内部函数在外部调用时仍能访问那些本该“消失”的变量。
闭包形成的三个必要条件
不是所有嵌套函数都是闭包。真正构成闭包需同时满足:
- 一个函数(inner)定义在另一个函数(outer)内部
- inner 函数体内明确访问了 outer 函数的局部变量(或参数)
- outer 函数执行后,返回 inner 函数(或以其他方式让 inner 在 outer 作用域外被调用)
如何通过闭包从外部读取内部变量
本质是把内部函数作为值返回,再在外部调用它。此时它仍带着对原作用域的引用:
示例:
立即学习“Java免费学习笔记(深入)”;
function createCounter() {
let count = 0; // 局部变量,本应在函数结束时销毁
return function() {
count++; // 访问并修改外部变量
return count;
};
}
const counter = createCounter(); // createCounter 执行完毕,但 count 没被回收
console.log(counter()); // 1
console.log(counter()); // 2
这里 counter 就是一个闭包——它记住了 createCounter 执行时的词法环境,包括那个 count 变量。
常见误区与实际效果
注意几个关键点:
- 变量不是“拷贝”,而是持续被引用,所以值会累积变化(如上面的 count)
- 每次调用外层函数都会创建独立的作用域,因此多次调用生成多个互不干扰的闭包
- 即使外层函数已执行完、栈帧已弹出,其局部变量仍保留在内存中,直到闭包不再被引用
为什么能“访问”而不仅仅是“读取”
闭包提供的是对变量的引用,不是只读快照。这意味着你不仅能读,还能改、能增、能删——只要内部函数逻辑允许:
比如给闭包附加一个修改方法:
function createBox(initial) {
let value = initial;
return {
get: () => value,
set: (v) => { value = v; },
reset: () => { value = initial; }
};
}
const box = createBox(10);
console.log(box.get()); // 10
box.set(99);
console.log(box.get()); // 99
这个对象里的每个方法都是闭包,共享同一份 value 环境。


















