今天被同学问了一个 bug,属于平时很难察觉的方面,特地写篇文章介绍一下。
起因
今天被同学求助,帮忙看看代码有什么问题,想要实现的功能很简单,就是一个登录窗口的弹出与关闭。但遇到了 bug,窗口无法关闭。
代码展示如下,省略了部分内容,你能发现其中的问题吗?
<div class="top">点击,弹出登录框</div>
<div class="box">
<span class="close">关闭</span>
<div class="title">登录</div>
</div>
var top = document.querySelector('.top')
var box = document.querySelector('.box')
var body = document.body
var close = document.querySelector('.close')
top.addEventListener('click', function () {
box.style.display = 'block'
body.style.background = '#b2b2b2'
})
close.addEventListener('click', function () {
box.style.display = 'none'
body.style.background = ' '
})
起初我看代码感觉逻辑挺正确的,看他的测试结果事件也正确绑定了
折腾了一会,直到让他把代码发给我,亲自调试,才发现其中的猫腻
打印 top 变量,指向的竟然是 window ???
每次点击关闭,都因为事件冒泡,又把关闭的窗口打开了
解决办法很简单,变量改个名就行了
但这种情况我也是第一次遇见,十分诡异,就研究了其原因
原因
说到底,这是因为 var 所导致的 bug
我们知道,var 声明的变量都是绑定到 window 身上,我们使用这些变量省略 window.
的部分,然而 window 身上自带了一些变量,它们是不可改写的。
而 var 声明已有的变量,也不会报错。这就导致我们以为声明了一个变量并正确赋了值,实际上使用的却是浏览器内置的 window 属性。
而 window.top
属性,返回的是窗口层级最顶层窗口的引用,是个不可改写的属性。也就导致在之前代码中声明 top 节点绑定事件,事件实际却绑定到了 window 身上。
window 身上的属性很多,高达 200 个,其中不乏我们平时常用的变量名,name
length
parent
self
等。只是比较幸运的是,这些变量都是可写的,我们声明它们并使用,不会引发什么错误。
为什么又说是因为 var 导致的呢?因为如果你使用 let 定义 top 变量,会直接报错
let top = 1 // SyntaxError: Identifier 'top' has already been declared
对于那些可写的 window 属性,let 可以正常声明并赋值,不会修改 window 上的属性,使用时也是用 let 声明的变量。
解决
这种问题。不经常遇到,但真遇到了,很难排查出来。
window 属性那么多,以后还会不断新增,搞不好什么时候就遇到了。
在此提出一个解决办法:一是要拒绝使用 var
只用 let
const
来声明变量,二是开启 JavaScript 文件的检查功能。
在 vscode 中,可以在 setting.json 文件中设置 "js/ts.implicitProjectConfig.checkJs": true
来启用 JavaScript 文件的语义检查。
这个语义检查功能很好用,能帮你排查 js 文件中不合规范的地方,比如参数类型,变量是否已声明等等,有助于我们养成良好的代码习惯。
结语
如果文中有错误或不严谨的地方,请务必给予指正,十分感谢。
如果喜欢或者有所启发,希望能点赞关注,鼓励一下作者。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。