提到 JavaScript 的“闭包”,很多人第一反应是:
👉 “函数里套函数,然后内部函数能访问外部变量”。
但真相远不止如此。今天我们要揭开的,是 闭包的本质,以及与之紧密相连的 执行上下文。
1. 什么是闭包?别再误解了!
闭包这个词最早出现于 1964 年,原意是“带有环境的函数”。
换句话说:
闭包 = 函数 + 它的执行环境。
在 JS 中,每个函数都是闭包。区别在于:
- 普通函数只用到局部变量;
- 闭包函数则依赖外部环境的变量。
所以 function 天生就是闭包,而不是“必须嵌套才能叫闭包”。
2. 执行上下文:JS 的“运行沙盒”
执行上下文是 JS 执行一段代码所需的全部信息,包括:
- 词法环境(Lexical Environment) :存储标识符与值;
- 变量环境(Variable Environment) :处理
var声明; - this 值:决定函数内部
this的指向; - 作用域链:逐层解析变量;
- Realm:所在的全局对象和内置对象集合。
可以把它想象成函数运行时的一套“宇航服”,函数需要的氧气、压力、温度,全靠它提供。
3. var 的坑:为什么要发明 IIFE?
var 声明的变量是函数作用域,不会被 {} 块级作用域限制。
for (var i = 0; i < 3; i++) {}
console.log(i); // 3
为了制造新的作用域,人们发明了 IIFE:
;(function() {
var a = 1;
console.log(a);
})();
这招虽然管用,但副作用也不少,比如和 with 一起使用时,会导致变量解析混乱。
4. let:救火队员
ES6 引入的 let,带来了真正的块级作用域:
if (true) {
let x = 10;
}
console.log(x); // ReferenceError
相比之下,var 时代的“作用域黑洞”终于得到修复。
5. Realm:多全局环境的黑科技
在浏览器里,不同的 iframe 其实有不同的全局对象和内置对象集合。
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var b1 = iframe.contentWindow.Object;
var b2 = Object;
console.log(b1 === b2); // false
这就是 Realm 的概念:每个 Realm 拥有一套独立的内置对象。
跨 Realm 交互时,instanceof 判断可能失效。
6. 为什么要理解这些?
- 闭包:帮你理解函数“为什么能记住外部变量”;
- 执行上下文:让你清楚 JS 执行时到底依赖哪些信息;
- var / let / Realm:揭示了语言设计的演进和坑点。
掌握这些,不仅能写出更优雅的代码,还能在面试中轻松拿捏“闭包题”。
总结
- JS 中所有函数都是闭包,区别只是是否用到外部变量。
- 执行上下文是运行时的“全套装备”,包含作用域链、this、变量环境等。
- IIFE 是
var时代的应急方案,let则从根本上修复了作用域问题。 - Realm 让我们认识到“多全局环境”的复杂性。
互动问题:
👉 你平时更喜欢用 var 还是 let?为什么?
欢迎留言,聊聊你的选择!