js一个"a" in window引发的问题

2,854 阅读4分钟

故事背景

今天看了tom大叔的一篇文章 还有司徒正美的文章,其中讲到一些关于javascript的小技巧的题,比较有趣,都有一些绕,不过很好理解。

但是只有一道题让我琢磨半天,并不是很难,但是从一开始分析方向就错了,所以记录下来分享。

if (!("a" in window)) {
    var a = 1;
}
console.log(a)

就这样的一道题,问的就是a输出什么?

一些老的javascript选手已经做出了答案,输出是一个undefined。但究竟为什么是undefined呢?

最开始我思考,因为Javascript没有块级作用域,所以if里面定义的var a = 1;一定是会传入到window对象里面的,所以a的输出一定是1。当然这道题不可能这么简单,想当然的就把这几行代码输入到控制台,输出一下,发现输出的是undefined

赤裸裸的打脸让我羞红了脸庞,怎么会这样,难道 in 操作符有什么诡异的操作吗?赶紧搜了一下in操作符,赶紧查看一下,有没有什么不为人知的js小技巧。

很快撇清了怀疑,因为并没有关于操作符引发的诡异事件。既然如此,那一定是发生在window"a"上面了!

为了清晰的查看发生了什么,我把var a 改成了var b,发现居然奇迹般地可以输出正确结果了。令人诧异的同时,也让我发现了看来和里面的 var a 有关,于是我定睛一看,发现并不是 in 操作符的问题,而是 var的问题

var 导致的变量提升

题目已经给出了答案,本题本质上是 var 带来的变量提升

// var a; 已经存在
if (!("a" in window)) {
//并没有进到这里
    var a = 1;
} else {
//进入了这里
}
console.log(a)

同时引出另一个问题,最初忽略变量提升最大的原因在于if (!("a" in window))这句代码,本身是一个判断,a是否存在于window对象当中。因此,即使a === undefined 返回的也是true

既然布尔值是true,其实也就走不到var a = 1;这一步了。这道题令人迷惑的原因在于明明 if语句没能走下去,却依然被var a = 1;这句话带来的影响而造成问题。

变量提升带来的问题

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。在 ECMAScript® 2015 Language Specification 之前的JavaScript文档中找不到变量提升(Hoisting)这个词。不过,需要注意的是,开始时,这个概念可能比较难理解,甚至恼人。

例如,从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

由于if判断语句存在 window作用域下,因此变量提升依然生效,正因为没有块级作用域,所以存在于if中的var会提升到,window对象下。

总结

如此说来本题也就解开了,一道由变量提升引发的谜题。最初却被迷魂汤迷住了双眼,正是由于对if判断条件的信任才导致无法理解代码的走向。

这是一道很早之前的小题,我们在日常写代码的时候一般也会格外注意作用域问题,而且大多数情况不会使用in来判断。但假如真的需要使用,那就不得不注意变量提升导致的,in操作符判断的不确定性,一道小题,几分钟迷人眼,但如果是一个大型组件,一时半会还真不那么容易找到问题来源,浪费c干不说,还让代码的不确定性提升。