作者:温荣蛟

置信大部分有接触前端开发的敌人对var关键字十分的相熟,然而到古代工程中你会发现var关键字正在舍弃,有的甚至应用lint(eslint no-var)工具明确禁用var,var关键字正在隐没。

1.var的定义

MDN文档对var的形容是:

变量申明,无论产生在何处,都在执行任何代码之前进行解决。用 var 申明的变量的作用域是它以后的执行上下文,它能够是嵌套的函数,或者对于申明在任何函数外的变量来说是全局。如果你从新申明一个 JavaScript 变量,它将不会失落其值。

形容的第一句话提到的就是var的变量晋升个性,最初一句提到的是var变量可反复申明的个性。这两个个性给开发者带来便当的同时带来了一些变量取值的异样。如:

var a = 1;// 其余逻辑代码function a(){}console.log(a); // 1

以上代码中,先是申明了a变量,并且赋值为1,接着又申明了a函数。开发者本意是想执行a函数,但在开发中遗记曾经对a做过申明,导致a被赋值为1,出现异常。浏览器将以上代码解决为如下

var a;function a(){}a = 1;console.log(a)

2. var的个性与缺点

咱们在 var的定义 局部中曾经用一个示例演示了var变量晋升和变量从新申明个性及其给开发者带来的困扰。

2.1 变量晋升

js解析过程中因为变量申明(以及其余申明)总是在任意代码执行之前解决的,所以在代码中的任意地位申明变量总是等效于在代码结尾申明。这意味着变量能够在申明之前应用,这个行为叫做“hoisting”。“hoisting”就像是把所有的变量申明挪动到函数或者全局代码的结尾地位。

var a = 1;console.log(b); // undefinedvar b = 2;console.log(b); // 2

浏览器会将其解析为

var a;var b;a = 1;console.log(b);b = 2;console.log(b);

变量晋升看似很美妙,开发者能够在同一作用域任意地位申明变量。然而他也带来了一种潜在的危险,如:

var a = 1;function fn(){  // 开发者想拜访下层作用域中的a变量  console.log(a);  // 其余代码逻辑  // 在这里开发者遗记上方程序有拜访a变量,又申明了一个局部变量a  var a = {}  // 其余逻辑}

问题呈现了,开发者想要拜访的是a的值1,后果理论值是undefined,然而咱们调试断点时又不能直观的体现出在函数部分作用域中a曾经申明了。

2.2 变量从新申明
应用var关键字申明的变量能够从新申明,并且不会失落其值

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

从新申明,值仍是1。

同样的,过于宽松的申明规定,带来了一些问题,当然只是从新申明对原变量是没有影响的,只是从新申明之后咱们个别都会配置赋值逻辑。这会带来什么问题呢,咱们仍然看第一节中的例子

var a = 1;// 其余逻辑代码function a(){}console.log(a); // 1

a被从新申明过了,然而开发者没有感知,导致的是a又被赋值成了1。

2.3 没有块作用域

在JS中作用域分为全局作用域和部分作用域,创立部分作用域的形式es5之前只能通过function() {}函数创立,即在es5之前'{}'是不会创立作用域的,这也导致了if/while 等等语句中申明的变量也会晋升到作用域顶端

if (0) {    var a = 1;}console.log(a); // undefined

这给开发者带来了微小的困惑,不可达代码竟然能影响程序。

在该示例中浏览器将其解决为

var a;if (0) {    a = 1}console.log(a);

在JS中应用一个未声明的变量时,浏览器会将其挂载到window对象下。如何证实以上示例是变量晋升了而不是挂载到window顶层对象上了呢。咱们改变一下示例

"use strict"; if (0) {    var a = 1;}console.log(a); // undefined

咱们开启严格模式(strict mode),咱们晓得严格模式下是不能应用未声明的变量的。而a变量的值仍然是undefined,阐明浏览器的确是将条件循环语句中的var申明变量做了申明晋升。

{

var a = 1;

}
console.log(a); // 1

3. 替代品 let const

3.1 javascript发展史

咱们下面举了很多例子来演示var的个性,也都体验过var可能带来的额定异样。所幸的是,ECMA标准制订者们也在一直的欠缺与丰盛JS语法,下表列举了JS版本的工夫节点和次要变更内容

作为一个前端开发者对es6的重要性都有粗浅的了解。咱们看看es6(es2015)带来了些什么。class、extend的引入,和Array Object对象办法的丰盛咱们暂且抛开不谈。 咱们来看看let、const 关键字的引入。

3.2 let

MDN给let的形容是:

let容许你申明一个作用域被限度在 块级中的变量、语句或者表达式。与 var 关键字不同的是, var申明的变量只能是全局或者整个函数块的。

3.21 比照一下var
与var的变量晋升绝对应的,let存在暂存死区的一个特色,即 通过let 申明的变量直到它们的定义被执行时才初始化。在变量初始化前拜访该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化解决的“暂存死区”中。

console.log(a);  // ReferenceErrorlet a = 1;

let申明的变量会创立块级作用域

{    let a = 1;}console.log(a); // ReferenceError

不能反复申明

let a = 1;let a = 2;  // Uncaught SyntaxError: Identifier 'a' has already been declared

这就从语法上限度了变量笼罩的可能。

特地提一下,在 switch 语句中只有一个块,会呈现以下谬误。

let x = 1;switch(x) {  case 0:    let a;    break;  case 1:    let a; // Uncaught SyntaxError: Identifier 'a' has already been declared    break;}

应用 {} 块,会创立一个块作用域,就不会呈现上述反复申明的问题

let x = 1;switch(x) {  case 0: {    let a;    break;   }  case 1: {    let a;    break;  }}

咱们回忆一下咱们之前模块化解决的经典伎俩,应用一个自执行函数包裹模块代码。基于let语法的块作用域个性,咱们解决模块化是不是多了一种间接应用 '{}' 包裹的模式。当然古代前端工程中曾经以单文件作为模块划分了,并不需要咱们代码自行处理模块化。

3.3 const

此申明创立一个常量,其作用域能够是全局或本地申明的块。 与var变量不同,全局常量不会变为 window 对象的属性。须要一个常数的初始化器;也就是说,您必须在申明的同一语句中指定它的值(这是有情理的,因为当前不能更改)。

const和let的个性十分相似 - const申明的变量援用值不可更改; - const申明变量必须在申明时初始化。

4. 总结
var申明变量晋升和可反复申明的个性,很容易呈现变量笼罩造成值异样。 let const是一种语法更加严格的设计,从语法自身就做出了束缚,防止了变量笼罩的问题,同时也创立了更为平安的块作用域。

综上,开发中倡议对立应用es6语法,舍弃var关键字的应用。退出no-var规定吧,拥抱let const。

5. 参考文档
VAR形容https://developer.mozilla.org...
LEThttps://developer.mozilla.org...
CONSThttps://developer.mozilla.org...
MDNhttps://developer.mozilla.org...
no-varhttps://eslint.bootcss.com/do...
ES6https://www.w3cschool.cn/ecma...