当for循环里有名为onclick函数时,window会被加上点击事件, 为什么 ???

<script type="text/javascript">
for (var i = 0; i < 3; i++) {
    function onclick(e) {
        document.write(222)
    }
}
</script>

点这里测试

如上代码,会被加上点击事件,莫名奇妙

而且将onclick不放在for循环里时,并没有这个现象

阅读 3.7k
4 个回答

这似乎是2个问题:

step1. 块级作用域内定义函数,相当于使用var定义一个函数

阮一峰 es6 中有提及
image.png

那么代码可以看成

var onclick = function (e) {
    document.write(222)
}
for (var i = 0; i < 3; i++) {
    
}

step2. var定义变量相当于在添加全局属性(window.x

那么,就得到如下代码

window.onclick = function (e) {
    document.write(222)
}
for (var i = 0; i < 3; i++) {
    
}

显而易见,这个就是给window绑定事件了
应该就是这2个原因了


ps: 我查了下window.xvar x的区别

@scort 提及,唯一的确保在于configurable属性,这就是为什么var定义的变量使用delete window.x删除
image.png

MDN对configurable的描述
image.png

这个应该跟浏览器解析块级函数声明有关。理论上来说函数不应在块级作用域中声明
https://developer.mozilla.org...

从ECMAScript 6开始,在严格模式下,块里的函数作用域为这个块。ECMAScript 6之前不建议块级函数在严格模式下使用。

你现在就是在for语句中声明了函数,在chrome、firefox中应该是等效为window.onclick = ....
如果你在IE中查看就不会有这样的效果。
chrome控制台提供了一个API来查看事件绑定。看下图:
当onclick在全局中声明时,就是一个正常的全局函数,并不会绑定window.onclick
736.png

而如果是在for语句中声明则会绑定事件:
苏宁豆芽图片20200601194817.png

所以,千万不要在块级语句中声明函数,这个行为很奇怪

window.onclick 就是相当于注册点击事件

然后看下这个例子:

window.abc = null

function abc() {
  console.log(1)
}

console.log(window.abc) // null

再看下这个例子,括号里是不是就是你发的代码:

window.abc = null

 {
  function abc() {
    console.log(1)
  }
 }

console.log(window.abc) // abc

你可以理解 浏览器在最开始把window.onckick赋值为null,如果在声明函数的时候不是在大括号内,函数就会声明提示导致声明在window.onclick = null前面就无法重新赋值

感觉是 chrome 的 bug。

我们来做一个实验。(以下结果来自 Chrome/83.0.4103.61)

var _a = null;
Object.defineProperty(window, 'a', {
 configurable: true,
 enumerable: false,
 get () {return _a;},
 set (v) {_a=v;console.log('set a to', v)}
});

window.a = 1; // set a to 1
window.a // 1

var a = 2 // set a to 2
window.a // 2

{ var a = 3; } // set a to 3
window.a // 3

{ function a(){} } // set a to f a(){}
window.a // f a(){}
window.a === window._a // true

function a(){} // nothing
window.a // f a(){}
window.a === window._a // false
Object.getOwnPropertyDescriptor(window, 'a') // 已经不是我们第一步 define 的 内容了

由此可见,顶层代码块中的 var / function delcaration 会触发 window 上对应 property 的 setter,如果没有则 define 一个,而直接在顶层的 var / function declaration 则会直接触发 window 对象上的 defineProperty,如果已经存在且 configurable 为 true 则覆盖,为 false 则报错。

我们看看 window.onclick 是什么,

Object.getOwnPropertyDescriptor(window, 'onclick')
// {
// configurable: true
// enumerable: true
// get: ƒ onclick()
// set: ƒ onclick()
// }

chrome 中 onclick 是通过 setter 来完成事件注册的, 猜测 setter 内部调用了 addEventListener。 顶层的 function onclick(){} 会调用 define property, 而顶层代码块中的 function onclick(){} 则会调用 set property,本质和 window.onclick = ... 是一回事。 个人觉得顶层代码块中的 var / function 既然也会提升,那也应该使用 define 语义。

这玩意儿真是 JS 的糟粕。 两个糟粕碰一起就更加莫名其妙了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题