let命令
用于声明变量,但其声明的变量只作用与当前代码块中:
{
let a=1
console.log(a) //1
}
console.log(a) //a is not defined
let在循环体中的使用:
使用var声明变量i循环语句输出:
for (var i = 0; i < 3; i++) {
console.log(i);
}
//0 1 2
使用var声明变量i循环语句异步方法输出:
for (var i = 0; i < 3; i++) {
setTimeout(()=>{
console.log(i);
},100)
}
//3 3 3
使用let声明变量i循环语句异步方法输出:
for (let i = 0; i < 3; i++) {
setTimeout(()=>{
console.log(i);
},100)
}
//0 1 2
总结:由于var声明的变量i是全局范围的,所以每次循环都是改变的同一个变量i的值,当循环体中存在异步执行的方法时,输出的值将会是变量i循环之后的最终结果。而这种情况下使用let声明变量i就可以避免这个问题。
let不存在变量提升及暂存性死区
“变量提升”即变量可以在声明前调用,值为undefined。
但let声明变量必须在使用之前,否则代码会报错
“暂存性死区”:在某个块级作用域中(这里之所以说块级作用域中而不是代码块中,之后会有说明),只要存在let或const声明的变量时,这个变量就将不受来自作用域外部的影响。而在当前作用域中且在let或const声明变量之前使用了该变量,这段代码将会报错。
不受外部影响
var = 123;
if (true) {
let ;
console.log() //undefined
}
代码块中的“死区”
if (true) {
console.log(a); // ReferenceError
let a;
console.log(a); // undefined
a = 666;
console.log(a); // 666
}
较为隐蔽的“死区”
某些块级作用域并不是单纯的代码块,而是隐藏在函数参数中
function bar(a = b, b = 2) {
return [a, b];
}
console.log(bar()); // 报错
function bar(b = 2, a = b) {
return [a, b];
}
console.log(bar()); // [2, 2]
不允许重复声明
let不允许在同一块级作用域下重复声明同一变量, 如果你这么做了,程序将会报错,同时在函数中使用let声明与函数参数同名的变量也是不行的(除非此声明处于另一个单独的块级作用域中)。
function func(a) {
let a;
}
func() // 报错
function func(a) {
{ let a; }
}
func() // 不报错
块级作用域
块级作用域存在的意义?
1.是由于变量提升导致代码块内部变量影响到了整个作用域
var a=6
function func() {
console.log(a) //undefined
{
var a;
}
}
//燃鹅这里的var换成let就能正常输出a的值了
var a=5
function func() {
console.log(a) //5
{
let a;
}
}
2.在执行循环操作是,计数变量使用后并不会随着循环方法的结束而销毁,而是成为了全局变量。
for (var a = 0; a < 6; a++) {
console.log(a);
}
console.log(a); // 6
es6块级作用域,每一层都是一个单独的作用域, 可任意嵌套,匿名自执行函数也不再需要了,可以直接使用{}来替代了
{
let i = 1; {
let i = 2; {
let i = 3; {
let i = 4; {
let i = 5;
console.log(i) //5
}
console.log(i) //4
}
console.log(i) //3
}
console.log(i) //2
}
console.log(i) //1
};
块级作用域与函数声明
es5规定函数只能在顶层作用域或函数作用域中声明,但es6出现了块级作用域的概念,那么问题来了,块级作用域中能声明函数吗?
理论上来讲,es5是不支持这种做法的,但是浏览器为了兼容旧版代码,所以在使用了这种用法时浏览器并不会报错,并且能正常运行
if (true) {
function f() {
console.log('1111') //1111
}
f()
}
es6引入了块级作用域的概念,同时也支持了函数声明在块级作用域中的用法。但是在块级作用域中声明的函数如同被let声明的变量一般,无法在作用域之外的地方使用。
{
function f() {
console.log('I am inside!');
}
}
f();
按照es6的规定来讲上面的代码中的f是未定义的函数,但实际上我在Google浏览器中运行的结果是输出了'I am inside!'。
这里我们就不得不提到es6浏览器为了兼容老代码所做的兼容性处理了
- 允许在块级作用域内声明函数。
- 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
- 同时,函数声明还会提升到所在的块级作用域的头部。
由于你无法确定代码运行环境是es6还是es5,所以我们应该尽量避免在块级作用域内声明函数,如果一定要这么做的话,建议是写成函数表达式。
// 块级作用域内部的函数声明语句,建议不要使用
{
let a = 'secret';
function f() {
return a;
}
}
// 块级作用域内部,优先使用函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
const命令
不同于let命令,const命令声明一个只读的变量,也可以称之为常量,一旦声明不可更改,因此const命令声明时,必须在声明时就为其赋值,否则就会报错。
同let命令一样,const只在声明的块级作用域中生效,同时也存在暂存性死区,也不允许重复声明。
const a;
// SyntaxError: Missing initializer in const declaration、
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const命令本质上是保证其声明的变量所指向的内存地址所保存的数据不能改动,
如果对于简单数据类型而言(如:数值、字符串、布尔值),值就等同于常量无法改变。
但如果对于引用类型而言,变量所指向的内存地址,保存的仅仅是一个指针,所以只能保证这个指正指向的地址是固定的,
至于指针所指向地址中的内容就是不可控的了。
const hahaha = {
a: '666',
b: '888'
}
hahaha.a = '999'
console.log(hahaha) //{a: '999', b: '888'}
顶层对象的属性
顶层对象在浏览器中是指window对象,而node中指的是global对象,在es5中 顶层对象属性和全局变量环境是等价的
window.a = 1;
a // 1
var b = 2;
console.log(window.b) // 2
当然,es5中的这种情况,也被一众程序员所诟病。
因此,let、const命令声明的全局变量不再属于顶层对象的属性了。
window.a = 1;
a // 1
let b = 2;
console.log(window.b) // undefined
globalThis对象(ES2020引入的概念)
对于不同的运行环境,globalThis对象都代表顶层对象,
当然,全局环境下使用this也可以取到顶层对象,
但由于this指向是能够发生改变的,但globalThis在所有情况下都能取到顶层对象