JavaScript 变量声明 var、let、const 区别

笔记哥 / 05-21 / 1点赞 / 0评论 / 640阅读
曾经 `var` 如帝王一般统治过 JS 的变量声明,直到后来大佬们实在是忍不了 `var` 那稀烂的声明规则,便引入了 `let` 和 `const` 这两大刀斧手,`var` 被轻轻松松的斩落马下,如今,再难看见 `var` 的身影。 ## 变量声明 在 JS 中,所有变量都可以用 `var`、`let` 和 `const` 这三个关键字声明。 ```js // 字符串 var name1 = '前端路引'; let name2 = '前端路引'; const name3 = '前端路引'; // 数字 var age1 = 18; let age2 = 18; const age3 = 18; // 布尔值 var isDev1 = true; let isDev2 = true; const isDev3 = true; // 空值 var null1 = null; let null2 = null; const null3 = null; // 数组 var arr1 = [1, 2, 3]; let arr2 = [1, 2, 3]; const arr3 = [1, 2, 3]; // 对象 var obj1 = {}; let obj2 = {}; const obj3 = {}; // 函数 var fn1 = function () { console.log('前端路引'); } let fn2 = function () {}; const fn3 = function () {}; ``` 以上的各种声明方法都是有效的,JS 语法也没强制要求必须用某个关键字声明变量。 ## 区别 它们之间有哪些恩恩怨怨,为什么会在新的语法中引入 `let` 和 `const` 来取代 `var` 呢? ### 作用域 var 申明的变量,拥有函数作用域,在整个函数体内部都能访问。 let 和 const 申明的变量是块级作用域,出了代码块(就是 `{}` 包裹的代码片段)就不可以再访问了。 ```js function funcScope() { if (true) { var a = 1; // 函数作用域 let b = 2; // 块级作用域 const c = 3; // 块级作用域 } console.log(a); // 1(var 穿透了 if 代码块) console.log(b); // ReferenceError: b is not defined console.log(c); // ReferenceError: c is not defined } ``` ### 变量申明提升 var 申明的变量,不论在哪个位置申明,都会提升到作用域顶部。 let 和 const 申明存在 `暂时性死区(TDZ)`,这些名词道道记不住也罢,反正您只要知道在申明之前使用会报错就对了。 ```js console.log(x); // undefined(变量提升) var x = 10; console.log(x); // 10 console.log(y); // ReferenceError: y is not defined(暂时性死区) let y = 20; ``` var 申明的变量,在申明之前使用会获取 `undefined`,这就是所谓的 `变量申明提升`。 ### 重复声明 var 允许同一个变量重复声明。 let 和 const 禁止重复声明。 ```js var a = 1; var a = 2; // 允许 console.log(a) // 2 let b = 1; let b = 2; // SyntaxError: Identifier 'b' has already been declared const c = 1; const c = 2; // SyntaxError: Identifier 'c' has already been declared ``` let 和 const 重复声明同一个变量,会报语法错误。 ### 全局作用域下的行为 如果在全局作用域中使用 var 申明一个变量,那么这变量会变成全局对象(如 window)的一个属性。 let 和 const 则不会绑定到全局对象。 什么是全局作用域?比如新建一个 a.js,使用 `` 引入这个 JS 文件到 html 页面中,那么这个 JS 文件最外层就称之为全局作用域。 **新建一个 test.html,引入 a.js:** ```html ``` **新建一个 a.js:** ```js // 全局作用域 var globalVar = 1; console.log(window.globalVar); // 1 let globalLet = 2; console.log(window.globalLet); // undefined function test () { // 函数作用域 }; { // 块级作用域 } ``` ### 可变性 var 和 let 声明的变量可以重新赋值。 const 声明的是常量,必须初始化且不可重新赋值(但对象/数组的内容可修改)。 ```js var str = '前端路引'; str = 'web3dev'; // 允许修改变量 let num = 1; num = 2; // 允许修改变量 const PI = 3.14; PI = 3; // TypeError: Assignment to constant variable.(不可重新赋值) const obj = { a: 1 }; obj.a = 2; // 允许(修改属性) obj = {}; // TypeError: Assignment to constant variable.(不可重新赋值) ``` 为什么 const 申明的数组和对象可以修改? 这就涉及到 `引用` 这个概念了,可以理解为 `数组` 和 `对象` 就像现实中的房子。const 申明就像给房子打上了标签贴,拥有了这个房子的使用权,但这房子不止 const 一个拥有者,房子的主人还能对房子进行室内装修改变。const 的标签贴只能使用一次,要想把这标签再贴在另一座房子,那么就行不通了!! ### 循环 var 申明的变量会泄露到循环体外部。 let 仅作用于循环体内部。 ```js for (var i = 0; i < 3; i++) {} console.log(i); // 3 for (let j = 0; j < 3; j++) {} console.log(j); // ReferenceError: j is not defined ``` let 解决异步回调问题: ```js // var 的问题 for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i)); // 输出 3, 3, 3 } // let 的解决方案 for (let j = 0; j < 3; j++) { setTimeout(() => console.log(j)); // 输出 0, 1, 2 } ``` ## 写在最后 最佳实践推荐:所有变量声明优先考虑使用 `const` ,如果确定变量需要修改再使用 `let`。 果您的代码确实需要兼容 2017 年之前的浏览器,那么使用 `var` 吧,别考虑 `const` 和 `let` 了。当然也可以使用 `babel` 等编译器,将 `const` 和 `let` 转换成 `var` 兼容低版本浏览器。