JavaScript的执行机制——块级作用域

本文讨论了ES6之前JavaScript的变量提升问题,如何导致代码执行异常,以及ES6引入let和const解决这个问题的过程,通过实例说明了块级作用域如何避免变量提升带来的困扰。
摘要由CSDN通过智能技术生成

ES6 之前是不支持块级作用域的,因为当初设计这门语言的时候,并没有想到 JavaScript 会火起来,所以只是按照最简单的方式来设计。没有了块级作用域,再把作用域内部的变量统一提升无疑是最快速、最简单的设计,不过这也直接导致了函数中的变量无论是在哪里声明的,在编译阶段都会被提取到执行上下文的变量环境中,所以这些变量在整个函数体内部的任何地方都是能被访问的,这也就是 JavaScript 中的变量提升。


变量提升所带来的问题

=========================================================================

由于变量提升作用,使用 JavaScript 来编写和其他语言相同逻辑的代码,都有可能会导致不一样的执行结果。那为什么会出现这种情况呢?主要有以下两种原因。

变量容易在不被察觉的情况下被覆盖掉


比如下面JavaScript 代码如下:

var myname = “极客时间”

function showName(){

console.log(myname);

if(0){

var myname = “极客邦”

}

console.log(myname);

}

showName()

执行上面这段代码,打印出来的是 undefined,为什么输出的内容是 undefined 呢?我们再来分析一下。

首先当刚执行到 showName 函数调用时,执行上下文和调用栈的状态是怎样的?这里我就直接展示出来了,最终的调用栈状态如下图所示:

在这里插入图片描述

showName 函数的执行上下文创建后,JavaScript 引擎便开始执行 showName 函数内部的代码了。首先执行的是:

console.log(myname);

执行这段代码需要使用变量 myname,结合上面的调用栈状态图,你可以看到这里有两个 myname 变量:一个在全局执行上下文中,其值是“极客时间”;另外一个在 showName 函数的执行上下文中,其值是 undefined。那么到底该使用哪个呢?

相信做过 JavaScript 开发的同学都能轻松回答出来答案:“当然是先使用函数执行上下文里面的变量啦!”的确是这样,这是因为在函数执行过程中,JavaScript 会优先从当前的执行上下文中查找变量,由于变量提升,当前的执行上下文中就包含了变量 myname,而值是 undefined,所以获取到的 myname 的值就是 undefined。

本应销毁的变量没有被销毁


接下来我们再来看下面这段让人误解更大的代码:

function foo(){

for (var i = 0; i < 7; i++) {

}

console.log(i);

}

foo()

如果你使用 C 语言或者其他的大部分语言实现类似代码,在 for 循环结束之后,i 就已经被销毁了,但是在 JavaScript 代码中,i 的值并未被销毁,所以最后打印出来的是 7。

这同样也是由变量提升而导致的,在创建执行上下文阶段,变量 i 就已经被提升了,所以当 for 循环结束之后,变量 i 并没有被销毁。

这依旧和其他支持块级作用域的语言表现是不一致的,所以必然会给一些人造成误解。


ES6 是如何解决变量提升带来的缺陷

=================================================================================

上面我们介绍了变量提升而带来的一系列问题,为了解决这些问题,ES6 引入了 let 和 const 关键字,从而使 JavaScript 也能像其他语言一样拥有了块级作用域。

关于 let 和 const 的用法,你可以参考下面代码:

let x = 5

const y = 6

x = 7

y = 9 //报错,const声明的变量不可以修改

从这段代码你可以看出来,两者之间的区别是,使用 let 关键字声明的变量是可以被改变的,而使用 const 声明的变量其值是不可以被改变的。但不管怎样,两者都可以生成块级作用域,为了简单起见,在下面的代码中,我统一使用 let 关键字来演示。

那么接下来,我们就通过实际的例子来分析下,ES6 是如何通过块级作用域来解决上面的问题的。

你可以先参考下面这段存在变量提升的代码:

function varTest() {

var x = 1;

if (true) {

var x = 2; // 同样的变量!

console.log(x); // 2

}

console.log(x); // 2

}

在这段代码中,有两个地方都定义了变量 x,第一个地方在函数块的顶部,第二个地方在 if 块的内部,由于 var 的作用范围是整个函数,所以在编译阶段,会生成如下的执行上下文:

在这里插入图片描述

从执行上下文的变量环境中可以看出,最终只生成了一个变量 x,函数体内所有对 x 的赋值操作都会直接改变变量环境中的 x 值。

所以上述代码最后通过 console.log(x) 输出的是 2,而对于相同逻辑的代码,其他语言最后一步输出的值应该是 1,因为在 if 块里面的声明不应该影响到块外面的变量。

既然支持块级作用域和不支持块级作用域的代码执行逻辑是不一样的,那么接下来我们就来改造上面的代码,让其支持块级作用域。

这个改造过程其实很简单,只需要把 var 关键字替换为 let 关键字,改造后的代码如下:

function letTest() {

let x = 1;

if (true) {

let x = 2; // 不同的变量

console.log(x); // 2

}

console.log(x); // 1

}

执行这段代码,其输出结果就和我们的预期是一致的。这是因为 let 关键字是支持块级作用域的,所以在编译阶段,JavaScript 引擎并不会把 if 块中通过 let 声明的变量存放到变量环境中,这也就意味着在 if 块通过 let 声明的关键字,并不会提升到全函数可见。所以在 if 块之内打印出来的值是 2,跳出语块之后,打印出来的值就是 1 了。这种就非常符合我们的编程习惯了:作用域块内声明的变量不影响块外面的变量。


JavaScript 是如何支持块级作用域的

=====================================================================================

现在你知道了 ES 可以通过使用 let 或者 const 关键字来实现块级作用域,不过你是否有过这样的疑问:“在同一段代码中,ES6 是如何做到既要支持变量提升的特性,又要支持块级作用域的呢?”

那么接下来,我们就要站在执行上下文的角度来揭开答案。

你已经知道 JavaScript 引擎是通过变量环境实现函数级作用域的,那么 ES6 又是如何在函数级作用域的基础之上,实现对块级作用域的支持呢?你可以先看下面这段代码:

function foo(){

var a = 1

let b = 2

{

let b = 3

var c = 4

let d = 5

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以领取

技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。

技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以领取

[外链图片转存中…(img-0ca3WET4-1712928204399)]

  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值