let
和const
命令是ES6新增的,让我们来看看它们是怎么回事吧~
在有 let
和const
之前,我们使用 var
来声明变量,var
声明变量会有变量提升的特性,
console.log(a)
var a = 1 //undefined
用var
声明a
,a
变量会被提升到在当前作用域的最前面声明,其实执行的代码是这样的:
var a;
console.log(a)
a = 1;
为了约束变量提升,出现了块级作用域,块级作用域搭配let
和const
就没有变量提升了。
console.log(b)
let b = 1 //ReferenceError: b is not defined
用let
就没有变量提升的问题,但是在声明变量之前就访问,会报错,访问b
会报ReferenceError
错误
那let
为什么没有变量提升的问题呢?块级作用域又是什么呢?
块级作用域
let
和const
实际上为ES6引入了"块级作用域"的概念。
在ES5中只有全局作用域和函数作用域。ES6新增了块级作用域来约束变量的生命周期。
简单说就是两个大括号{}
就形成了块级作用域,外层代码块不受内层代码块的影响。
function f1(){
let n = 5;
if(true){
let n = 10
}
console.log(n) //5
}
for(let i =0 ;i< 10 ;i++){
}
console.log(i) //ReferenceError: i is not defined
内层的n
不会影响外层的n
,所以打印n=5
for循环中声明了变量i
,在for循环之外访问i
变量,会报错,这是因为在外层代码块中没有声明变量i
for循环中都会有块级作用域。我们来看一个经典的例子
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[2]() //10
console.log(i) //10
在for循环外可以访问到i
,i
的值为10,这是因为i
在for
循环中使用var
声明了,会被变量提升,结果就是i
挂载到全局对象中,
在浏览器中就是window
对象,成为window
的属性。每一层循环改变的都是全局的i
, 传入到循环中的console.log(i)
都是10,
最后外部访问的i
值也变成了10
var brr = []
for (let i = 0; i < 10; i++) {
brr[i] = function () {
console.log(i)
}
}
brr[2]() //2
console.log(i) //ReferenceError: i is not defined
for
循环中使用let
声明变量i
,这样每一层循环都重新声明了i
,并绑定了当前循环的代码块,所以传入内存函数的额console.log(i)
也是当时的i
的值,所以brr[2]()
执行时,变量i
的值是2,在for
循环外访问i
会出错。
你可能会问,每一层的循环变量都是重新声明的,那么for循环怎么知道上一层循环的值,从而计算出本层的值?这是因为
Javascript引擎内部会记住上一层的循环的值,在上一层循环的基础上,计算得出初始化本层的变量的值。
细说for循环中的块级作用域
for循环中()
是一个块级作用域,而循环体内{}
是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面输出三次 abc ,说明,循环内部的i变量与循环变量i
不在同一个作用域,有各自单独的作用域。
我们来看看let
到底是怎么由var
转过来的,我用Babel
试试:
var brr = []
for (let i = 0; i < 10; i++) {
brr[i] = function () {
console.log(i)
}
}
brr[2]()
console.log(i)
Babel
转过之后,
"use strict";
var brr = [];
var _loop = function _loop(_i) {
brr[_i] = function () {
console.log(_i);
};
};
for (var _i = 0; _i < 10; _i++) {
_loop(_i);
}
brr[2]();
console.log(i);
babel把for循环内层都抽象到一个函数中了,然后,把循环变量传入这个函数参数,这样每一层的循环都会返回一个函数声明,
而循环变量都会锁定住。
暂时性死区
只要块级作用域中有let
和const
命令,那么所声明的变量就"绑定"这个区域,不再受外部的影响。
在块级作用域内,使用let
或const
声明变量之前,该变量都是不可取的,这就叫"暂时性死区"
暂时性死区解决的问题:
解决了在声明变量之前访问该变量,没有变量提升,主要为了减少运行时错误。
暂时性死区的本质:
只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,不可访问,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
const
基本用法
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const 也不会变量提升
const 也存在暂时性死区特性
- const在声明变量时就要赋值,否则以后不能赋值了
- const只声明不赋值会报错
本质
const 声明的变量如果是基本类型,那么不允许改变,如果是引用类型,那么只要不改变引用的地址就是可以的。
const name = 'Friday'
name = 'Sunday' //直接报错
const people = {
name: 'Jack',
age: 21
}
people.age = 22
console.log(people) //{ name: 'Jack', age: 22 }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。