在JavaScript中,声明变量我们经常使用var,而对于另外声明变量的关键字let和const,我们现在进行讲解。let和const是es6的新内容。var,let和const有相似之处,但又各有特点。
目录
let
let的块级作用域
解释
先看代码:
{
var a=10
let b=20
}
console.log(a);//10
console.log(b);//报错
结果:
如图所示:打印a是没有问题的,但打印b会报错。
因为b是由let声明的,let存在块级作用域 ,使用let声明变量就会和当前的花括号绑定,只在当前花括号内有效,花括号外不能使用此变量。
应用:
由这个特性我们可知:
let很适合用在for循环中
ES5中变量可以重复声明,存在变量提升,所以使用for循环时变量i会泄露成全局的变量i
我们先在for循环中使用var来定义变量。
//使用var在for循环中声明变量
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i);
}
}
console.log(i);
arr[3]()
console.log('这里是分界线');
//使用let在for循环声明变量
var arr1 = []
for (let j = 0; j < 10; j++) {
arr1[j] = function () {
console.log(j);
}
}
arr1[6]()
arr1[7]()
arr1[8]()
arr1[9]()
console.log(j);
分界线上面是for循环内用var定义变量的输出结果,下面是let的输出结果。
可见,我们在使用var关键字定义变量的时候,我们无法输出数组每一项的值,只能是最后的值。因为上面代码var定义的变量i相当于一个全局变量,for循环每一次都会改变i的值,最后输出i的值就是for循环结束时候i的值,我们无法使用调用函数的方式拿到数组arr中某一项的值,只能拿最后一项。
而我们使用let在for循环里面声明一个变量时候,for()的小括号没相当于存在一个隐藏的块级作用域,每次循环js都会把let初始化并且重新赋值,保证let声明的变量不会被for循环一次次的覆盖。所以我们在上面代码中可以通过函数调用的方式打印出数组任意一项的值。
最后的报错是也是因为块级作用域的原因,我们无法使用console.log(j);去访问上面let定义的j,因为超出了它的作用域。
拓展
ES6中允许块级作用域的嵌套
如:
{ { { { { { { let a = 10 } console.log(a); } } } } } }
这个语句会报错,let声明的变量在罪内层的花括号内,它的作用域就在这个括号内,我们在这个括号外是无法访问这个let声明的a的。
再看一个:
function fun() {
function fn() {
if (true) {
let a = 20
console.log(a);//20
}
console.log(a);//baocuo
}
fn()
}
fun()
结果:
第一个打印语句成功打印了a的值,因为他就在let声明变量a的花括号内,而第二个打印语句在a的作用域外,自然不可以打印出来。
ES6中块级作用域必须有花括号
如下书写方式会直接报错
let不存在变量的提升
我们使用var定义一个变量的时候:
function fun() {
console.log(a);
var a = 10
}
fun()
结果:
可见,var声明的a出现了变量的提升,a提升到了它作用域的顶端,但此时相当于未赋值的状态,使用打印出来是undefined,在下面又给它赋值为10.
那么let会是如何呢?
function fun() {
console.log(tmp);
let tmp = 10
}
fun()
结果:
出现了报错,因为let声明的变量不存在提升,所以我们的打印输出的语句压根找不着这个变量tmp,就会报错。
let不能重复声明同一个变量
先看var重复声明变量并赋值:
var a = 1;
var a = 2;
console.log(a);
结果:
后面的a覆盖前面的a,没有问题。
那么let重复声明变量的后果:
会直接报错。
暂时性死区 ---TDZ
使用let声明变量就会和当前的花括号绑定,变量正常应该先声明后使用,如果使用let声明变量之前使用了变量就会存在暂时性死区简称TDZ
所以,变量应该先声明后使用
const--声明常量
和let基本类似,存在暂时性死区,先声明再使用,存在块级作用域,不可以重复声明同一个变量等,但const是声明常量时候用的,有自己的特点。
声明常量,不可更改
如:先使用var声明一个变量
var a = 10
a=20
console.log(a);
结果:
声明之后我们可以给他重新赋值更改其值。
但const就不行了
const a=10
a=20
console.log(a);//声明常量不可以更改
结果报错:
const只要声明就必须初始化
如果不赋值,直接就报错:
const拓展:关于不可修改的问题
const声明的变量,不可更改,其实不能改变的是该常量的内存地址
使用const定义的变量保存的是一个地址值,这个地址指向一个对象引用。const保证这个地址值是不可变的,但对象本身是可变的,所以可以变更这个对象内部的属性。
例如:
const obj = {
name: '123'
}
obj.name = 'zhansgan'
console.log(obj);
结果:
我们使用const来声明一个对象obj。我们修改其name为zhansgan。打印输出居然没有报错,但const声明的东西明明不可以改变的啊。
因为属性名和地址是存储在栈内存,const保证栈内的地址指向不变,真正的数据存储在堆内存中,我们直接去堆内存里修改内容,就不会影响const。
Let,const与var的总结
var声明变量会出现变量提升,但let和const不会出现
var可以重复声明,let和const不可以
let和const的作用域在其声明的代码块里面,var声明的变量声明在全局时,可以任意拿到使用
let,const存在tdz暂时性死区
es6中块级作用域必须有花括号
const与let相似
const声明后就得赋值,声明的是一个常量。不可重复声明。
const声明的变量,不可更改,其实不能改变的是该常量的内存地址
使用const定义的变量保存的是一个地址值,这个地址指向一个对象引用。const保证这个地址值是不可变的,但对象本身是可变的,所以可以变更这个对象内部的属性。