EventListener作用域捕获:let与var在Script作用域的差异原因
问题描述
我编写了一段为HTML元素绑定事件监听器的代码,监听器的回调函数会与其词法环境形成闭包。代码如下:
var submit = document.getElementById("submit"); var x = 0; function attachEventListener(element, x){ let count = 0; let el = document.getElementById("counter"); element.addEventListener('click', (e) => { e.preventDefault(); count++; el.innerHTML = count + ` ${x}`; }); }
我的疑问是var与let变量在Script作用域下的捕获差异:将submit和x声明为var时不会被Script作用域捕获,声明为let时则会。想请教为何事件监听器会如此创建Script作用域?我曾尝试两种声明方式,原以为在事件监听器的作用域捕获行为上一致,虽了解let是块级作用域、var是全局/函数作用域,但未预料到Script作用域的差异。
问题解答
1. Script作用域对var和let的本质处理不同
- 用
var在Script标签内声明的变量,会直接挂载到浏览器的全局对象(window)上,属于全局对象的属性,并不存在于Script专属的独立词法环境中。 - 用
let/const在Script标签内声明的变量,会被存储在Script级别的独立词法环境里,这个环境属于全局作用域的一部分,但和全局对象相互分离——这些变量不会成为window的属性。
2. 闭包的捕获逻辑只针对被引用的作用域
闭包只会捕获其词法作用链中实际被代码引用的作用域:
- 当全局变量用
var声明时,如果回调需要访问全局变量,JS引擎会直接从全局对象(window)上读取,不需要依赖Script专属的词法环境,因此闭包不会捕获这个Script作用域。 - 当全局变量用
let声明时,全局变量只存在于Script专属的词法环境中,回调如果要访问它,就必须将这个Script作用域纳入闭包的作用链,所以你会看到Script作用域被捕获。
另外需要注意:你当前代码里的回调引用的x是attachEventListener函数的参数,属于函数作用域的变量,和全局的x无关——这种情况下不管全局x用var还是let,都不会影响这个回调的闭包结构。你测试中看到的差异,应该是在回调直接引用全局x的场景下才会出现。
内容的提问来源于stack exchange,提问作者Ends of Invention
相关产品推荐
相关产品推荐

