var、let和const的区别详解

  let 和 const 是 ECMAScript6 新推出的特性,其中 let 是能够替代 var 的“标准”,所以我们探讨 var、let 和 const区别,首先应该知道 var 到底有什么不规范的地方,或者是说有什么弊端。

var 的 特性 变量提升

  var 是 Javascript 用来定义变量的一个关键字,这是一个简单的变量定义方式

var a = 0;

  但是如果我们在定义这个变量之前,查询这个 a 的话,其实是不会报错误的

console.log(a); // undefined var a = 0; console.log(a); // 0

  虽然它输出了 undefined ,但这并不是我们想要的,我们希望在变量初始化前,是无法访问这个变量的,这虽然是一个约束,但是能让你的程序变得更可捉摸与维护。而 var 之所以能在var a = 0;前被访问,是因为这一句话在编译的时候其实是按以下的顺序进行的:

var a; console.log(a); // undefined a = 0; console.log(a); // 0

  这就是为什么第二行 a 会打印出 undefined 的原因了,这个叫做变量提升,使用 var 初始化变量的时候,在该作用域的开始,会先定义这个变量,再在后面进行赋值(初始化)。所以甚至你可以这么玩儿:

console.log(a); // undefined a = 0; console.log(a); // 0 var a;

  这样也是可以成功打印的,所以大家看到这应该明白了 var 会对程序眼维护造成的困扰吧? a 明明一路看下来没有看到的,居然依旧正常使用?!!这个时候还需要从该作用域查看到底有没有隐藏的var a;如果没有的话那还需要去作用域外边寻找。

console.log(a); // undefined let a = 0; console.log(a); // 0 var 的作用域

  我们再来看一下以下代码

for(var i = 0; i < 5; i++){ var a = 0; } console.log(i); // 5 console.log(a); // 0

  我们定义在 for 循环块中的变量 i、a 居然在循环外都能被取到,这显然并不规范。

  那么这里可以再给大家提一个面试经常也会提到的题目

for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); // 5 5 5 5 5 }, 300); } for (let j = 0; j < 5; j++) { setTimeout(function () { console.log(j); // 0 1 2 3 4 }, 300); }

  几乎一模一样的代码,就因为var、let的作用域区别,得到的结果大不一样。对于第一个for循环来说,i在全局范围内都有效,在setTimeout函数中没有找到i的定义,所以在全局里才能找到变量i,在for循环执行完之后,event loop中的,setTimeout中的匿名函数开始执行,这个时候的全局i其实已经是5了,所以全部打印出了5。
  而对于第二个for循环来说,let有块级作用域的概念,且值得注意的是for循环头部的let声明有一个特殊的行为,这个行为指出变量在循环过程中不止被声明一次,每次迭代都会被声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。这使得我们的j,每次执行其实都是块级作用域中的不同的一个变量,自然就不是最终的5了。
  所以如果在不能使用E6的时候,可以使用闭包来创建一个新的作用域。这个闭包能保留对它被声明的位置所处的作用域的引用,将i强行容留在自执行函数的作用域中使其不被回收。这样就能获取到每次的i值。

for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i) // 0 1 2 3 4 }, 300) })(i) } var 可重复定义

  这个很好理解,var是允许定义两次的,不报错误。

var a = 0; console.log(a); // 0 var a = 1; console.log(a); // 1

  编辑器会在判断有已经声明的同名变量时,会忽略 var 关键字,然后直接赋值,所以如果重复使用的一个声明有一个初始值,那么它是一个赋值语句。如果重复使用的一个声明没有一个初始值,那么它不会对原来存在的变量有任何的影响。这对于 ES5 之前的 JS 是合法的,但是 ES6 后,认为这种重复定义的做法是不科学的, let 和 const 皆不允许作用域内重复一个定义同名变量。

现在我们来讲讲 let 暂存死区

  首先,绝对是不允许在let定义变量前使用这个变量的

console.log(a); // ReferenceError: a is not defined let a = 0; console.log(a); // 0

  let声明不会被提升到当前执行上下文的顶部,从该块级作用域开始,到初始化位置,称作“暂存死区”,所以在对于a的暂存死区中使用a会报Reference错误。

块级作用域

  let声明的变量拥有块级作用域,在块级作用域外是访问不到该变量的,而var不同,在for循环中定义的变量可以在外部访问到,这会导致一些意想不到的bug。注意,大括号 {} 是块级作用域,不是说函数作用域。

禁止重定义 let a = 0; console.log(a); // 0 let a = 1; console.log(a); // SyntaxError: Identifier 'a' has already been declared window 对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxxsd.html