共计 3260 个字符,预计需要花费 9 分钟才能阅读完成。
作者:温荣蛟
置信大部分有接触前端开发的敌人对 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); // undefined
var 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); // ReferenceError
let 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…