作用域与执行上下文解析

本文详细介绍了JavaScript中的作用域和执行上下文。作用域是编程语言中存储和访问变量的规则,JavaScript采用词法作用域,变量查询遵循作用域链。执行上下文是JavaScript代码运行时的环境,分为全局执行上下文、函数执行上下文等,创建于代码编译阶段。在执行上下文中,词法环境和变量环境存储不同类型的变量,通过执行栈管理。在执行时,JavaScript引擎通过执行上下文实现变量查找,从而实现了作用域链的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、作用域

几乎所有语言最基本的功能之一就是能够储存变量当中的值,并且能够在之后对这个变量进行访问和修改,事实上正是这种储存和访问变量值的能力将状态带给了程序,那么因此程序语言需要制定制定这样一套规则来存储变量,同时能够方便在日后访问和修改这些变量,这套规则被称为作用域。
在介绍JavaScript中的作用域之前我们需要了解传统编译语言的编译过程:

  1. 分词/词法分析
  2. 解析/语法分析
  3. 代码生成

事实上JavaScript引擎中的编译过程要比这复杂得多,例如延迟编译、重编译等等措施来提升性能,但是我们在进行宏观分析得时候可以认为JavaScript的编译也是这样3个步骤。
在了解了上述编译过程的基础上我们来介绍JavaScript的作用域:词法作用域,词法作用域就是定义在词法阶段的作用域,换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的,因此当词法分析器处理代码时会保持作用域不变,因此词法作用域也被称为静态作用域。与之相对的就是动态作用域,词法作用域的定义过程发生在代码的定义阶段,而动态作用域是在运行时动态确定的,我们以如下代码进行区分:

function foo() {
   
	console.log(a); // 2
}
function bar() {
   
	var a = 3;
	foo();
}
var a = 2;
bar();

上面示例中调用foo函数打印的a为2,这是因为词法作用域让foo函数内部的a通过RHS引用(如果查找的目的是对变量进行赋值,那么就会使用LHS引用,如果查找的目的是获取变量的值,就会使用RHS引用)获取到了全局作用域中的a,因此控制台输出2,而如果JavaScript中的作用域为动态作用域的话foo函数在执行时将会输出3.,这是因为动态作用域并不会关心函数和作用域是如何声明以及在何处声明的,只关心它们在何处被调用,因此在动态作用域中由于foo函数在bar函数中被调用,因此RHS引用获取到了bar函数的作用域中的a。
当一个块或函数嵌套在另一个块或函数中时就发生了作用域的嵌套,也就是作用域链,此时如果在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量或抵达最外层作用域(也就是全局作用域)为止。

至此我们进行总结:作用域本质上是编程语言储存及访问变量的具体规则,这个规则为编程语言带来了状态,JavaScript与大多数编程语言相同选择了词法作用域,也被称为静态作用域,作用域定义在词法分析阶段,与函数在何处调用无关而仅仅只由我们在写代码时将变量和块作用域写在哪里决定。接下来我们引入编程语中的另一个语法概念:上下文,上下文是一段程序运行时所需要的最小数据集合,大家对于这个描述也看到了作用域与上下文之间的本质区别,作用域关注的是标识符(变量)的可访问性,而上下文指代的是整体环境,同时对于大部分编程语言来说作用域在编译过程中的词法分析阶段生成(词法作用域),并且不会改变,而上下文在运行时确定,随时可以改变,因此两者之间拥有着本质性的不同,但是JavaScript中的执行上下文却又与之不同,我们在下面详细介绍JavaScript中的执行上下文。

二、执行上下文

1、什么是执行上下文?

执行上下文是评估和执行 JavaScript 代码的环境的抽象概念,也就是JavaScript执行一段代码时的运行环境,每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如this、变量、对象以及函数等等。

2、执行上下文什么时候被创建

在引入JavaScript中执行上下文何时被创建的话题之前我们先关注另外一个问题:变量提升,所谓变量提升,是指在代码执行过程中,JavaScript引擎把变量声明部分和函数的声明部分提升到代码开头的“行为”,变量提升后会给变量设置默认值,这个默认值就是我们熟悉的undefined。对于变量提升我们已经很熟悉,那么我们来讨论它背后到底是怎么实现的?
从概念的字面意思来看,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,正如我们所模拟的那样,但,这并不准确,实际上变量和函数声明在代码里的位置是不会改变的,而是在编译阶段被JavaScript引擎放入内存中。与传统的编译语言例如C、C++等不同,JavaScript并不会先被编译生成第三方脚本然后运行,事实上JavaScript作为一门解释型语言,它是在宿主环境直接解释执行,比如下载完一个js文件,JavaScript会先编译这个js文件,但是js文件内定义的函数是不会编译的,等调用到该函数的时候,JavaScript引擎才会去编译该函数(编译过程会随着宿主环境的不同而不同,目前认可度高使用范围最为广泛的是V8引擎是基于编译器和解释器实现的代码编译)。
在上边我们提到JavaScript代码被执行时,会先进行编译,变量以及函数声明在这个阶段被JavaScript引擎放入内存中,实际上变量提升的内容保存在执行上下文中变量环境的对象中,一段JavaScript代码在经过编译后会生成两部分内容:执行上下文和可执行代码。我们以如下代码示例:

showName();
console.log(myname);
var myname = 'xiaozhang';
function showName(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值