故事背景
今天看了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干不说,还让代码的不确定性提升。