let 命令
块级作用域中的声明
for循环中的使用
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () { //这一行的i定义函数的时候绑定的,和var,let无关,不影响
console.log(i); //这一行的i是执行的时候才计算的,用var就只能找到10了
};
}
a[6](); // 6
//函数执行时的值是要去定义的时候找的,var的i是全局的,只能有一个,所以会被覆盖
//let的i是每次重新声明的,每个{}中都有一个,有10个不同的,所以不会互相影响
如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?
这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
// abc
// abc
// abc
}
for (let i = 0; i < 3; i++) {
i = 'abc';
console.log(i);
}
// abc 只输出一次
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
寻找变量i的值会就近原则,先在自己的作用域找,如果没有再去父作用域找。赋值操作会将’abc’赋值给父作用域的i,然后打印’abc’,然后运行
Boolean('abc' < 3)
得到false,跳出循环,所以只输出一个abc
Boolean('abc' < 3)
的过程是:字符串‘abc’由于不能转换成合理的数值,会被转换为NaN,而任何操作数与 NaN 进行 关系比较,结果都是 false
let命令不存在变量提升
- 它所声明的变量一定要在声明后使用,否则报错。
暂时性死区
- 如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
- 在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
在作用域内,在let声明之前调用let声明的变量都会因为“死区”报错
// 不报错
var x = x;
// 报错
let x = x;
// ReferenceError: x is not defined
- 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
let不允许在相同作用域内,重复声明同一个变量。
- 不能在函数内部重新声明参数。
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
块级作用域
- 内层作用域可以定义外层作用域的同名变量。
- 内层作用域可以给外层作用域的同名变量赋值。
{
let a = 5
{
let a = 10
}
console.log(a); //5
}
{
let a = 5
{
a = 10
}
console.log(a); //10
}
ES6规定块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
浏览器实际块级作用域内声明的函数,行为类似于var声明的变量,即会提升到全局作用域或函数作用域的头部,然后给函数名变量名赋值为undefined。
应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
- ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
- let只能出现在当前作用域的顶层
// 第一种写法,报错
if (true) let x = 1;
// 第二种写法,不报错
if (true) {
let x = 1;
}
const 命令
- 对于const来说,只声明不赋值,就会报错。
- const只是不改变指针地址,简单数据类型指针直接指向相应的值,复杂数据类型指针指向真实数据的地址,所以
const obj = {}
中obj的内容可以改变。 - 如果真的想将对象冻结,应该使用Object.freeze方法。
- 彻底冻结对象时,应该深度遍历每个属性,对每个属性也进行冻结。
顶层对象
- var命令和function命令声明的全局变量,是顶层对象的属性
- let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
- 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。