JavaScript机制:变量&函数声明提升的那些事

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的其他特性,我们下回分解~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值