6、闭包与执行上下文:你以为懂了,其实没懂!

79 阅读2分钟

提到 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?为什么?
欢迎留言,聊聊你的选择!