JavaScript循环中意外变量捕获疑问:求上下文环境运行机制解析
解析JavaScript循环中意外变量捕获的问题
核心问题:var的作用域特性
这里的意外捕获根源在于**var声明的变量没有块级作用域**——它的作用域是整个函数(如果在全局环境下就是全局作用域),而非for循环的每一次迭代块。
具体运行机制拆解
- 代码开头用
var声明的e,其作用域覆盖了整个包含循环的上下文(全局的话就是全局作用域),整个循环过程中只有同一个e变量在被反复赋值。 - 循环遍历
elements时,每次迭代都会把当前DOM元素赋值给e,但这只是修改同一个变量的取值,而非创建新的变量。 - 给每个
<a>标签绑定的onclick事件处理函数handle是一个闭包,它会引用外部作用域的e变量。注意:闭包捕获的是变量本身,不是变量当前的取值——也就是说,它保存的是对这个变量的引用,而非快照。 - 当用户点击任何一个
<a>标签时,循环早已执行完毕,此时e的值已经被更新为最后一个遍历到的DOM元素,所以所有点击事件都会弹出最后一个元素的id。
解决方法
有两种常见的修复方式:
- 用
let替代var:let支持块级作用域,每次循环迭代都会创建一个全新的e变量,每个handle函数捕获的都是当前迭代专属的e变量,点击对应元素会弹出正确的id:
var elements = document.getElementsByTagName('a'); for (let e of elements) { e.onclick = function handle() { alert(e.id); } }
- 使用立即执行函数(IIFE)创建独立作用域:通过IIFE把当前迭代的
e作为参数传入,在独立作用域中保存当前值,避免闭包捕获同一个变量:
var elements = document.getElementsByTagName('a'); for (var e of elements) { (function(currentE) { currentE.onclick = function handle() { alert(currentE.id); } })(e); }
内容的提问来源于stack exchange,提问作者korsunskyroma
相关产品推荐
相关产品推荐

