ES5
如果你还在使用ES5,JavaScript有时候会产生一些令人摸不到头脑的Bug
这是因为ES5有些机制与我们敲代码的思考逻辑并不吻合,最为显著的就是变量提升
变量
console.log(tmp);
if (false){
var tmp = "hello world";
}
这个代码段能输出什么?按照正常“人类”逻辑,我们理解的语义,这段代码应该报错。
但是在ES5中,会输出undefined,为什么呢,上面这段代码实际上等同于:
var tmp;
console.log(tmp);
if (false){
tmp = "hello world";
}
是的,不管这个变量在哪进行初始化或声明,哪怕是在不成立的判断语句中(ES5没有块级作用域,if里外在声明方面是相同的),它的声明语句也会被提升到作用域最上端。
上面这个例子可能在实际开发中遇到较少,但是下面这个问题我们很有可能碰到:
var tmp = setInterval("console.log(1)",1000);
function f(){
console.log(tmp);
if (true){
clearInterval(tmp)
var tmp = "";
}
}
这种情况下,在此function作用域里,你就会发现,tmp莫名变成了undefined,这就是变量提升+内部变量覆盖外部变量的结果。
所以,当你使用ES5,发现变量莫名其妙变成undefined的时候,要首先排除变量提升的问题。
同样,有一个经典问题:
var liList = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}
这时点击每个li都会打印5,因为i的声明提升了,而后面console.log的也是i的地址而已,循环后,这个值将是5。
函数
说完了变量,那么函数声明会不会提升呢?我们看下面这个例子
var a=function(){
console.log('a');
}
function b(){
console.log('b');
}
相信你已经猜到了,其中a和变量声明一样,也会被提升,并且也是undefined。
但是b不同,如b声明函数,会连带声明和赋值一同被提前到作用域顶部(在顶部的顺序取决于代码顺序),所以在该函数代码块前,也可以调用该函数。
但是,如果是这样呢:
a();
if(true){
function a(){
console.log('a');
}
}
在严格的ES5标准,if下声明函数是会报错的,因为没有完全块级作用域。
但是一般浏览器会进行兼容,此时a和变量一样,在顶部声明,在if条件成立下赋值。
ES6
变量、块级作用域
经过升级,块级作用域终于被支持,在上面ES5中的实例(请注意,ES6中已经不推荐var的声明方式,取代的为let和const)
console.log(tmp);
if (false){
let tmp = "hello world";
}
将得到不同的结果,终于报错了,因为此时tmp的生命周期终于被局限在if语句中,代码块外无法获取内部变量,轻松实现“闭包”。
let终于不再存在变量提升,并且有了块级作用域
const基本相同,不过作为常量,指针地址不可改变,指向对象的话,对象的属性是可以改变的。
函数
在ES6中,函数的逻辑只变了一部分,首先,块级作用域的出现,导致在if中声明函数不会报错了,if(false)、当然也包括if(true)里的函数不会被外部调用了。
但是函数的声明依然会被提升到块级顶部。
a();
function a(){
console.log('a');
}
依然是可用的。
至于ES6的其他特性,我们下回分解~