js块级作用域:为什么要引入let和const?
作者:互联网
前言
前面我已经讲了js的调用栈是怎么工作的,以及js引擎是如何解析和执行js代码的,正是由于这些前面所讲的一些特性.才引起了js的一些问题,或者说js的设计缺陷
前面我们说过,js只有全局作用域和函数作用域,这对于我们一些学过其他高级语言,比如java或者c语言的人来说,写js代码时总是会遇到一些我们直觉难以理解的bug,正是由于这种缺陷,es6之后通过引进let
和const
关键字并且设计了词法环境,来避免产生这种因缺陷引起的bug,但是js又必须向下兼容,所以很长时间段内,我们必须理解变量提升等特性,当然这也会提高你对js的理解
作用域
作用域:是指程序中定义变量的区域,该位置决定了变量的生命周期.通俗理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性与生命周期
es6之前,我们讲过函数只有两种作用域全局和函数
- 全局作用域中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期
- 函数作用域就是在函数内部定义的变量或者函数,并且定义好之后只能在函数内部访问,函数执行结束之后,函数内部的变量会被引擎回收
在es6之前,只有这两种作用域,而其他语言都普遍支持块级作用域.块级作用域就是用**{}**包起来的一段代码,比如函数,判断语句,循环语句,甚至单独的一个{}都能被看作块级作用域,如下:
if(){}
while(){}
function(){}
for(){}
{}
如果一种语言支持块级作用域,那么代码块内部定义的变量,外部是访问不到的,并且内部代码执行完之后,内部的变量会被销毁.
但是为什么js代码在es6之前不支持呢?因为js设计者的本人开发这门语言的时候只是为了应付公司的项目,当时并没有想过能火起来,开发js的时候无疑省去块级作用域是最方便的
var 关键字带来的一些问题
我们看看下面这段代码
1.变量偷偷提升,你半天看不出来
var myname = '凯隐'
function showName() {
console.log(myname)
if (0) {
var myname = '拉亚斯特'
}
console.log(myname)
}
showName()
如果认真看完了我前面的博客留个心眼相信很容易就能回答出来
不过我们的直觉告诉我们这段应该打印什么呢?
应该打印凯隐
而事实是这两个打印语句打印的都是undefined
为什么呢?
因为showName函数中if块呢的代码变量提升了,所以这个函数作用域就先使用了这个函数作用域中的myname了,而内部的myname还没有赋值,是undefined
不过肯定好兄弟要问了:那为什么下面那个打印的还是undefined啊?不是if块中已经给myname赋值拉亚斯特了吗?
别急吗,这个等讲了词法环境,和js引擎查找变量的顺序之后再讲,先欠着
2.本该销毁的变量没有被销毁
function foo(){
for(var i=0;i<7;i++){
}
console.log(i) //7
}
本该销毁的i变量并没有被销毁 这就造成了一些内存泄漏问题
基于以上问题,es6之后引进了let,const关键字,来避免这些涉及到这些设计上的错误
let,const的引入
关于let 和const,请看以下代码
let x = 5
const y = 7
x = 6
y = 8 //报错,const声明的变量不能修改
从上面的区别来看,我们可以简单的描述以下let和const
let声明的是变量,是能够更改的值
const声明的是一个常量,不能更该=改,当你更改时,js引擎会报错
但是这里要注意的一个点是
const obj = {
a:123
b:456
}
obj.a = 789 //能够修改 不会报错
obj = '修改了obj的引用' //报错
需要注意的是,用const声明的引用型对象.常量只是保存了引用地址,也就是说修改这个常量会报错,但是你修改常量保存的引用地址里的对象是不会报错的,就好像,我手里有一个绳子,拴着一条狗,但是这条狗怎么变化我是不管的,我只要保证我的绳子栓着的是这条狗就行
再来看一段代码
function varTest(){
var x = 1
if(true){
var x = 2 //同样的x变量,引起变量提升
console.log(x) //2
}
console.log(x) //2
}
分析以上代码,会发现这个函数的执行上下文入栈之后,var声明的x变量发生了变声==变量提升,if块里面的也会,然后赋值操作x=1,再次赋值操作x=2,最后打印的也都是2,因为打印的x都是变量环境里面的x
但是引入了关键字le之后就不同了
function varTest(){
let x = 1
if(true){
let x = 2 //不同的x变量
console.log(x) //2
}
console.log(x) //1
}
从上面代码来看,使用了let之后js里面产生了块级作用域
没错,es6之后确实产生了块级作用域,但是块级作用域又是怎么实现的?
这里可能很少会有人去关注,我之前也没怎么关注,会用就行了,但是本着打破砂锅问到底的原则,还是去git,知乎,甚至外网上去找了以下,然后加上自己的理解给大家整合出来
首先我们得理解什么是词法环境
(这里提一嘴哈,没有证明存在词法环境和变量环境存在或者不存在,这只是一个抽象的说法,因为js引擎编译js代码是非常复杂得过程,一般为了方便理解才引入了这两种抽象说法,其实我觉得只要大家理解之后能去验证自己的理解是对的,什么词法环境或者变量环境自己起名叫阿猫阿狗也可以,也希望大家学习的时候,能多问问自己为什么)
词法环境和变量环境一样,也是js引擎编译js代码时候存放变量和常量的一块区域,只不过变量环境存储的是var声明的变量,词法环境保存的是let和const声明的变量和常量
可以理解为一个执行上下文包括:变量环境,词法环境,this(这个之后说)
js是怎么支持块级作用域的?
同一段代码中,js如何支持变量提升和块级作用域的呢?
那么我们就站在执行上下文的角度来讲讲,首先通过一段代码来讲解执行流程
function foo() {
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
先编译创建执行上下文和可执行代码,然后快速扫面代码,对于var声明的变量,变量提升阶段放入变量环境
此时变量环境
VariableEnviroment:{
a = undefined
c = undefined
}
当前词法环境:
LexicalEnviroment:{
b:uninitialized
}
通过上面的词法环境和变量环境,你有什么发现没有?
没错,let声明的b也变量提升了!
这时肯定有人说了:‘哎呀,这不对啊,不是说了let,const和没有变量提升吗?之前网上都是真么说的啊’
没错,网上大部分为了简便记忆,确实一直说let和const声明的变量没有变量提升,但是编译的时候内部是进行了变量提升的
不过有个特点就是,let声明的变量在你显示赋值之前,是不能使用的,简单的来说就是,let b声明了b之后,b进行了提升,这时候js引擎知道了,表示"我已经知道了你用let声明了一个变量b,我也在内存中为b预留了一个内存位置,但是在你显示赋值b具体内容之前,你不能使用它" 这就叫做暂存死区
这时候大家知道了吧,没错,let和const声明的量也是存在变量提升的,不过这种变量提升和var声明的变量提升不一样,在你没有对它显示赋值之前,你使用它是会报错的
所以在表现形式上来说,let和const声明的量表现得没有变量提升
,所以一般就直接得说他们没有变量提升了
好了 言归正传
以上代码流程
- 函数内部var声明得变量,编译阶段阶段,经过变量提升全部放入了变量环境中并赋值undefined
- 通过let声明得变量也提升了放入了词法环境,此时它没有值,而是一个状态uninitiated,表示预留了内存空间
- 注意,{}内的let声明的变量和没有参与变量提升,这就说明,
let和const声明的变量只在当前块内进行一个提升
, - 接下来,就是执行可执行代码了
首先
- 赋值操作a = 1,b= 2
- 然后执行到{}里面的内容,这时候词法环境又会为这个{}里的内容划分出一个新的区域(这区域也是个对象,其实这里也有个专属名词,不过我不记得了,理解了就好,这个区域可以理解为是保存b变量的对象的子域)
- 此时词法环境里又出现了一个新的区域,保存着这个块里的b,d变量,然后赋值操作,b=3,变量环境中的c赋值c=4,d=5,打印a = 1,b=3
- {}里的面代码执行完之后,为词法环境中为b,d创建的那块区域被销毁,打印b = 2,c=4(c是变量环境中的,只在函数执行完出栈时才会销毁),这时遇到了d,因为保存d的区域词法环境已经给销毁了,所以d会报错
d is not defined
通过我上面所说的两者结合,js就同时实现了变量提升和块级作用域了
总结
其实这篇挺简单的,由于var的变量提升存在变量覆盖,变量污染和内存泄漏等问题,于是引入了let,const关键字,同时js内部引入了词法环境这个概念来实现块级作用域.
下一篇博客我会讲作用域和闭包,这其实也是变量环境和词法环境的知识点
标签:块级,const,变量,作用域,js,词法,let 来源: https://blog.csdn.net/abcsxc258/article/details/122279496