let关键字特性
(1)不存在变量提升=>存在暂时性死区
暂时性死区会出现在使用 let 或 const 的代码中,使用 let 或 const 声明的变量会先在作用域中被创建出来,但此时还未进行赋值,如果此时对该变量进行求值运算,变量是不能被访问的,访问就会抛出异常。
因为有暂时性死区的存在,typeof 运算符也不再是绝对安全的,在 let 定义的变量之前使用 typeof 运算符同样会抛出异常。
if (true) {
// 暂时性死区开始
param = 'kingx';
console.log(param); // ReferenceError: Cannot access 'param' before initialization
typeof param; // ReferenceError: Cannot access 'param' before initialization
// ……
// 暂时性死区结束
let param;
}
(2)不能重复声明
在同一个作用域内,不能使用let重复声明相同的变量。
function foo() {
let arg1 = 'kingx';
if (true) {
let arg1 = 'kingx'; // 不影响
}
var arg1 = 'kingx'; // SyntaxError: Identifier 'arg1' has already been declared
}
function foo(arg1) {
let arg1 = 'kingx'; // SyntaxError: Identifier 'arg1' has already been declared
}
(3)不再是全局对象的属性
在ES6以前,在浏览器环境的全局作用域下,使用var声明的变量、函数表达式或者函数声明均是window对象的属性。
在ES6以后,依然遵循上述原则,但是如果是使用 let 声明的变量或者函数表达式,将不再是 window对象的属性。
// var声明的变量和函数表达式
var a = 1;
var fn = function () {
console.log('global method');
};
console.log(window.a); // 1
window.fn(); // global method
// let声明的变量和函数表达式
let b = 2;
let foo = function () {
console.log('global method');
};
console.log(window.b); // undefined
window.foo(); // TypeError: window.foo is not a function
let关键字好处
(1)不会导致for循环索引值泄露
在 for 循环中,因为循环的索引值一般只会在循环体内有效,所以当循环结束后索引值应该被回收。但是如果通过 var 定义索引值的话,该索引值在循环结束后仍然可以访问,此时使用let定义循环的索引值就很合适。
(2)避免出现变量提升导致的变量覆盖问题
ES6之前只有全局作用域和函数作用域,在函数作用域中通过 var 声明的变量会提升到函数作用域顶端,覆盖函数作用域里引用的全局变量(变量声明不受if(false)影响)
var arg1 = 'kingx';
function foo() {
console.log(arg1); // undefined
if (false) { // 变量声明不受if(false)影响
var arg1 = 'kingx2';
}
}
foo(); // undefined
(3)代替立即执行函数IIFE
立即执行函数(Immediately-Invoked Function Expression)的内部是一个独立的函数级作用域,使用IIFE的目的主要是避免污染当前作用域内的变量,而使用块级作用域则可以直接避免这个问题。
// IIFE 块级作用域写法
(function () {
let arg = ...;
...
}());
const关键字特性
(1)声明的值为一个常量,一旦声明将不会再改变。
对于基本类型的变量来说,变量就保存着内存地址的值,因此不能直接修改;而对于引用类型的变量来说,变量保存的是一个指向数据内存地址的指针,只要该指针固定不变,我们就可以改变数据本身的值。
(2)在声明时就必须初始化
使用const声明常量时,在声明时就必须初始化。如果只声明,不初始化,则会抛出异常。
const MAX = 16;
const MIN; // SyntaxError: Missing initializer in const declaration