var的缺陷

若是论正经的变量声明关键词,不抬杠那些 functionclass 等,在 ES6 之后,存在三个

  • const
  • let
  • var

推荐程度从上至下


var 作为一名老牌选手,陪着 JavaScript 出生到现在,但是它是存在问题的,当然在 ES6 之前,那是没得选,毕竟是声明变量唯一的存在

触目可及的一些问题

  • 提升
  • 全局属性
  • 重复声明
  • 没有块级作用域

提升

使用 var 声明时,会将声明语句提升至当前作用域的顶部,这导致可以提前使用变量,而不会报错,只是值是 undefined

这怎么说,要吃饭,先煮饭。提升则是,在煮饭完成前,就直接吃

console.log(a) // undefeind
var a = 10
console.log(a) // 10

实际的执行状况,会声明与赋值语句拆开

var a
console.log(a) // undefeind
a = 10
console.log(a) // 10

let 补上了这一点,杜绝此类事物的发生

若是在声明语句之前使用声明的变量,会导致异常直接终止程序

也就是在完成之前,强行打开高压锅,那就直接炸了

console.log(a) // ReferenceError  
- var a = 10
+ let a = 10  
console.log(a)

这种表现说专业点,叫做暂时性死区

从而影响了 typeof ,让它的使用,有了错误的可能性

全局属性

若是在函数之外声明变量,也就是全局作用域下使用 var ,会生成全局属性,挂载到 window 身上

var a = 10
window.a // 10

这种状况下,可能会导致覆盖掉 window 上的某个属性,还浑然不知

var open = false
window.open()   // TypeError

这种情况可以使用 IIFE 来回避

(function () {
  var open = false
  window.open()   // OK
})()

全局变量是魔鬼!


使用 let 可以很自然避开这一点,当然这不是在提倡在全局作用域下声明,不管怎样,都套上 IIFE ,而函数库默认通过 window 暴漏核心函数

重复声明

在同一个作用域下,可以存在多次声明

var a = 10
console.log(a) // 10
var a = 20
console.log(a) // 20

换成 let 再试试

- var a = 10
+ let a = 10
console.log(a)
- var a = 20
+ let a = 20
console.log(a)

没有输出,这种写法一旦存在,直接导致语法错误,没有跑的机会,早早扼杀这种低级错误

没有块级作用域

看一段循环,这情况会怎么样?

直觉告诉我,会是 1、2、3 ,而真实状况是 4、4、4

for (var i = 1; i <= 3; i++) {  
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}

循环属于微任务,而定时器属于宏任务

等待所有的微任务执行完毕之后,再以队列形式处理宏任务。但是此处没有块级作用域,它们所输出的是同一个 i ,并且是循环结束之后,不满足 i <= 3i ,于是输出了4

可以通过 IIFE ,来这段理想的操作

for (var i = 1; i <= 3; i++) {  
  (function(i) {  
    setTimeout(function() {  
      console.log(i)  
    }, 0)  
  })(i)  
}

慢着,为什么要这么麻烦,就不能拿变量记录一下吗

for (var i = 1; i <= 3; i++) {  
  var num = i
  setTimeout(function() {  
    console.log(num)  
  }, 0)  
}

不行,这导致了[[#重复声明]],看似有三个,但事实上操作的是同一个变量,于是变量 num 的值就成了3

可以看作是这样

var num = 1
num = 2
num = 3

console.log(num)  // 3
//...

何为块级作用域

块,指的是代码块

// 全局作用域
let a = 1;
(function () {
  // 函数作用域
  let a = 2;
  {
    // 块级作用域
    let a = 3
    console.log(a) // 3
  }
})();

这种写法就会让人很模糊,甚至说不该出现,然而 var 并不存在块级作用域,于是谁也说不准到底有没有

if (Math.random() > 0.5) {
  var a = 10
}
console.log(a) // ???

当存在块级作用域时,可以明确,这个条件不管是否成立,外界都无法拿到,可以放心报错

if (Math.random() > 0.5) {
- var a = 10
+ let a = 10
}
console.log(a) // ReferenceError

化解

回到前文,因为块级作用域的存在,可以自然达成想要的效果

for (let i = 1; i <= 3; i++) {  
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}

把循环拆开来看,就能明白为什么是块级作用域干的

{
  var i = 1
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}
{
  var i = 2
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}
{
  var i = 3
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}
{
  let i = 1
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}
{
  let i = 2
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}
{
  let i = 3
  setTimeout(function () {  
    console.log(i)  
  }, 0)  
}

留下一个片段提供思考,这会成为一个三连,还是顺子?

for (var i = 1; i <= 3; i++) {  
  let num = i  
  setTimeout(function () {  
    console.log(num)  
  }, 0)  
}

结尾

文中并没有去深究一些东西,只是在说明 var 不完美的一面。当然,就算是没有 letconst ,也早已可以自由把控这些不完美的方方面面

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值