let 命令是什么
let 是一个用来声明变量的命令。它的用法类似于var ,但是所声明的变量,只在let 命令所在的代码块内有效。
let 命令的特性?
- 所声明的变量的块内有效性
- 无初始化的变量提升
- 暂时性死区
- 不允许重复声明
变量提升是什么?
JavaScript引擎中,代码会在执行上下文中执行。而在执行上下文的创建阶段,声明的各种类型变量 (var, let, const, function, function*, class) 都会被添加到词法环境中。
let,const,class的「创建」过程被提升了,但是「初始化」没有提升。var的「创建」过程被提升了,并且会被「初始化」为undefined。function,function*的「创建」「初始化」和「赋值」都被提升了。function,function*提升的优先级别更高,更早被添加到词法环境中。let,const,class,function,function*被添加到词法环境中,而var被添加到变量环境中。- 变量环境也是一个词法环境,只用来存储
var变量绑定。
特别的for 循环
使用for循环时,发现let竟然可以重复定义同名变量:
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
总结:这表明函数内部的变量i 与for循环变量i 不在同一个作用域。循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
为什么需要块级作用域?
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
内层变量可能会覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
用来计数的循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
ES6块级作用域特点
-
ES6 允许块级作用域的任意嵌套
-
ES6 的块级作用域必须有大括号
-
内层作用域可以定义外层作用域的同名变量
{{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}}; -
匿名立即执行函数表达式部分功能被替代
// IIFE 写法 (function () { var tmp = ...; ... }()); // 块级作用域写法 { let tmp = ...; ... } -
允许在块级作用域之中声明函数
ES6 规定,块级作用域之中,函数声明语句的行为类似于
let,在块级作用域之外不可引用。- ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
- 浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,不会报错。
- 为减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器可以有自己的行为方式(类似于
var)。 - 考虑到环境导致的行为差异太大,应避免在块级作用域内声明函数。
- 严格模式下,函数只能声明在当前作用域的顶层。
const 命令的特点
-
const声明一个只读的常量。一旦声明,常量的值就不能改变。 -
只在声明所在的块级作用域内有效(与
let命令相同)。 -
const命令声明的常量也是无初始化提升,同样存在暂时性死区。 -
const声明的常量,也与let一样不可重复声明。 -
const仅仅保证指向的内存地址数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,
const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构则是可变的。 -
如果想冻结对象,应该使用
Object.freeze方法。
ES6 声明变量的六种方法
- var
- function
- let
- const
- import
- class
var命令和function命令是ES5 中仅有的两种声明变量的方法。let和const命令,import命令和class命令则是ES6新增的声明变量的方法。
全局变量与顶层对象
- 顶层对象,在浏览器环境指的是
window对象,在 Node 指的是global对象。 - ES5 之中,顶层对象的属性与全局变量是等价的。
- ES6 之中,
var命令和function命令声明的全局变量,依旧是顶层对象的属性。 - ES6 之中,
let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
顶层对象与全局变量等价的缺点?
- 没法在编译时就报出变量未声明的错误
- 容易不知不觉地就创建了全局变量
- 顶层对象的属性是到处可以读写的,这非常不利于模块化编程
- 顶层对象是一个有实体含义的对象(window指浏览器的窗口对象),含义混淆。
ES2020为什么新增globalThis对象?
JavaScript 语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。但是,顶层对象在各种实现里面是不统一的。
-
浏览器里面,顶层对象是
window,但 Node 和 Web Worker 没有window。 -
浏览器和 Web Worker 里面,
self也指向顶层对象,但是 Node 没有self。 -
Node 里面,顶层对象是
global,但其他环境都不支持。