本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-07-03
1.let
声明的变量命令只在其所在的代码块内有效。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
// 变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i
// 每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。
// 把var换成let 输出6
// 因为当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6
你可能会问,如果每一轮循环的变量i
都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值? 这是因为 JavaScript
引擎内部会记住上一轮循环的值,初始化本轮的变量i
时,就在上一轮循环的基础上进行计算。
2.let
不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
3.暂时性死区(TDZ)
只要块级作用域内存在let
命令,它所声明的变量就“绑定”这个区域,不再受外部的影响。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
// 在let命令声明变量tmp之前,都属于变量tmp的“死区”。
// 如果块级作用域内存在用let声明的变量 这let声明执行之前 访问该变量都会出错
// 在let之前的typeof也就没那么安全了
typeof x; // ReferenceError
let x;
// 没有let语句 则正常返回undefined
4.不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
5.为什么需要块级作用域
没有块级作用域会导致某些场景不合理:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量。
const
声明一个只读的常量。一旦声明,就必须立即初始化,不能留到以后赋值,也不可重复声明。
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
window
,但 Node
和 Web Worker
没有window
。Web Worker
里面,self
也指向顶层对象,但是 Node
没有self
。Node
里面,顶层对象是global
,但其他环境都不支持。兼容示例:
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};