如何理解JavaScript中的闭包与作用域【教程】

10次阅读

闭包javaScript作用域规则自然运行的结果,函数在定义时的词法作用域外被调用且仍访问该作用域变量即形成闭包;var循环中定时器输出相同值因共享同一变量绑定,let可解决;可通过chrome Scope面板或console.dir查看[[Scopes]]确认闭包。

如何理解JavaScript中的闭包与作用域【教程】

闭包不是语法糖,也不是高级技巧——它是 javascript 作用域规则自然运行的结果。只要函数在定义时所处的词法作用域之外被调用,且仍能访问该作用域中的变量,闭包就已形成。

为什么 var 声明的循环变量在定时器里总输出最后一个值

这是闭包最典型的“踩坑现场”。根本原因在于:所有迭代中创建的函数共享同一个 var 变量绑定,而循环结束时该变量值已固定。

for (var i = 0; i < 3; i++) {   setTimeout(() => console.log(i), 100); } // 输出:3, 3, 3
  • var 是函数作用域,整个 for 循环只声明一个 i
  • 三个 setTimeout 回调都闭包了外层函数作用域中的那个 i
  • 循环结束时 i === 3,所以全部打印 3
  • 改用 let 即可解决:每次迭代都会为 i 创建独立的绑定(块级作用域)

如何确认某个函数是否形成了闭包

看它是否「记住并持续访问」了定义时所在词法环境中的变量,哪怕该环境本应已销毁。chrome devtoolsScope 面板是最直接的验证方式。

  • 在函数内部打断点,打开 Scope 面板,若出现 Closure 条目,且列出外部变量(如 count, config),即存在闭包
  • 闭包变量不会被垃圾回收,直到闭包函数本身不可达
  • console.dir(fn) 在控制台展开函数对象,也能看到 [[Scopes]] 内含 Closure

闭包常被误用的三种场景

闭包本身无害,但滥用会引发内存泄漏或逻辑错乱。

立即学习Java免费学习笔记(深入)”;

  • 事件监听器中反复创建闭包并忘记 removeEventListener,导致 dom 节点和闭包变量长期驻留
  • 把大型对象(如整个 document 或组件实例)无意捕获进闭包,阻碍 GC
  • 构造函数中为每个实例方法都闭包私有数据——不如用 #privateFieldWeakMap 更清晰可控

真正难的不是写出闭包,而是判断「这个变量是否必须被闭包住」。多数时候,显式传参比隐式捕获更易追踪、更少副作用。

text=ZqhQzanResources