# 2. let与const
ES5及之前定义变量一般用var,ES6新增了let、const来声明变量,IE11+支持
var a = 1;
let b = 2;
# let与var的区别
var的特点:
- 允许重复声明变量
- 未经定义就可以使用(变量提升),默认为undefined
- 没有块级作用域。for循环或if里面的变量,在外部仍然可以访问。
- 函数或对象外部定义的变量都直接挂载到了window(self或this)全局变量上
let特性:
- 不允许重复声明变量
- 不存在变量提升,未经声明定义的变量不可以使用
- 块级作用域 (作用域范围:最近的一个大括号范围内{} )。
- 定义的对象不会挂载到window(self或this)全局变量上
- 暂存性死区
# 非块级作用域的问题
# 内层变量可能会覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world'; // 这里虽然在false循环里没有执行,但编译时由于var变量提升问题,tmp还是在函数内部被声明了。var tmp;改为let就可以避免这个问题
}
}
f(); // undefined
# for循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
# for循环里函数变量问题
// 没有块级作用域的经典问题
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function() {
return i
}
}
arr[0]() // 10
// 之前闭包的解决方法
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function(num) {
return function() {
return num;
}
}(i);
}
arr[0]() // 0
// 用let的解决方法
var arr = []
for (let i = 0; i < 10; i++) {
arr[i] = function() {
return i
}
}
arr[0]() // 0
# 块级作用域
{}大括号及for循环里用let声明的变量具有块级作用域,执行后会销毁。且不存在闭包变量的问题。
{
let a = 1; // let声明的变量具有块级作用域
var b = 1;
}
alert(a); // ReferenceError: a is not defined
alert(b); // 1
# let暂存性死区问题
ES6明确规定,如果区块中存在let和const命令,声明的对应变量是一个封闭的作用域。凡是在let声明之前使用变量,都会报错。
// 示例1
var tmp = 123
if (true) {
tmp = 'abc' // Uncaught ReferenceError: Cannot access 'tmp' before initialization
let tmp;
}
// 示例2
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错 当函数不传值时,y没定义就使用。Uncaught ReferenceError: Cannot access 'y' before initialization
// 示例3
let x = x; // 报错Uncaught ReferenceError: Cannot access 'x' before initialization
# const变量
const特性与let类似。const用来声明一个只读的常量,声明后的值无法修改。理论上由于引用类型的地址一致。const声明一个引用类型。只要不改变地址,值是可以改变的。
const a = {}
a.o = 1 // ok
a = {} // Uncaught TypeError: Assignment to constant variable.
const b = [] // ok
b.push(1,2,3) // ok
b = [] // Uncaught TypeError: Assignment to constant variable.
# 彻底冻结一个对象
如果想彻底冻结一个对象。可以使用Object.freeze()方法, 这样设置后对应的子属性也会无法修改
const foo = Object.freeze({})
foo.prop = 123 // 设置会不生效
// 彻底冻结对象及其子属性(需要递归)
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if (typeof obj[key] === 'object') {
constantize(obj[key]);
}
});
};
# JS声明变量的6种方法
ES5及之前只有两种声明变量的方法:var、function ES6新增了let、const、import(模块化)、class(类) 4种声明变量的方法
# 顶层对象
顶层对象有window、self、this、global(Node)
- var、function声明的全局变量,依旧是顶层对象的属性。
- let、const、class命令声明的全局变量,不属于顶层对象的属性。
let a = 1;
window.a // undefined
var b = 1;
window.b // 1
← 1. 前言 3. 变量的解构赋值 →