JavaScript声明变量有三种方式:var、let与const。
var声明变量
var name = "bob";
var声明变量的特点:
- 存在变量提升机制,JS引擎遇到var声明,会将其声明提到作用域顶端。
console.log(name);
var name = "bob";
- 覆盖全局对象存在的变量
var RegExp = "myRegExp";
console.log(window.RegExp); // 覆盖了正则表达式对象,输出 “myRegExp”
let声明变量
let声明变量的特点:
- 不允许重复声明变;
- 不存在变量提升机制;
- 只在当前作用域内有效;
- 临时死区(TDZ)- 使用未执行过的变量会报错。
let name = "bob";
const声明常量
const声明变量的特点
- 声明常量,声明时必须赋值;
- 不允许重复声明变;
- 不存在变量提升机制;
- 只在当前作用域内有效;
- 临时死区(TDZ)- 使用未执行过的变量会报错;
- 常量声明后不允许修改其值(下面有详细的说明)。
const USER_NAME = "Bob";
声明变量或者常量都会在内存中开辟一个内存地址来装载这个变量的值,const声明的常量后,就不允许改变这个常量所在的内存地址了,但是可以修改这个绑定的内存地址的值,比如来说,String类型的值是不可改变的,所以使用const声明一个字符串的常量之后就不允许修改了,相比声明了一个对象类型的常量,就可以修改其对象的值,因为修改的是其对象(绑定的常量的地址的值)的值,而不是修改绑定常量的地址。
const USER = {
name: 'bob',
nationality: 'China'
}
USER.name = 'Lynn';
console.log(USER); // 输出: { name: 'Lynn', nationality: 'China' }
// 错误:TypeError: Assignment to constant variable.
// 原因:修改了绑定的内存地址
USER = {
name: 'Lynn',
nationality: 'China'
}
console.log(USER);
变量提升机制与TDZ(临时死区)
JavaScript引擎在扫描代码变量声明时:
如果扫描到 var 声明的变量 => 会将其提升到作用域的顶端,此时其值是undefined;这就是var声明的变量提升机制。
&&
如果扫描到 let 或 const 声明的变量 => 会将其放入到 TDZ 中,如果访问TDZ中的变量就会报错,只有执行过了变量声明,变量才会从TDZ中移除,才能正常访问,这就是为什么作用域中存在let和const声明的变量,如果没执行到变量声明,会抛出错误的原因。
// var 变量提升机制
console.log(name); // undefined
var name = "bob";
// let 与 const 的临时死区(TDZ)
console.log(name); // ReferenceError: name is not defined
let name = "bob";
console.log(USER_NAME); // ReferenceError: USER_NAME is not defined
const USER_NAME = "bob";
实例-循环中的块级作用域绑定
由于在for循环中使用var声明变量,既没有存在块级作用域,也存在了一直共用同一个变量。
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function test() {
console.log(i);
}
}
arr[1]() // 10
arr[3]() // 10
arr[5]() // 10
arr[7]() // 10
// 为什么都输出是10,原因是arr[i]的各项函数不是立即执行的,在外面调用使用,for循环已经结束,i的值是10
使用立即执行函数能到达效果:
var arr = []
for (var i = 0; i < 10; i++) {
(arr[i] = function test(value) {
console.log(value); // 0,1,2,3,4,5,6,7,8,9
})(i)
}
使用let块级声明
var arr = []
for (let i = 0; i < 10; i++) {
arr[i] = function test() {
console.log();
}
}
arr[0]() // 0
arr[3]() // 3
arr[5]() // 5
arr[7]() // 7
// 为什么修改let声明后到达了预期中的效果?
// 原因:let声明的变量是存在块级作用域,每次循环的i其实都是一个新的变量
最后
ES6添加了let与const声明变量后,推荐声明变量时默认使用const,如果需要修改改变该值时,使用let声明变量,使工程师写JS代码时变得更加健壮。