【JavaScript】花点时间了解执行上下文

news2024/10/6 10:33:19

在这里插入图片描述

引言

当我们在浏览器中运行JavaScript代码时,浏览器会先创建一个全局执行上下文(Global Execution Context),然后逐行解析和执行代码。

执行上下文是JavaScript中非常重要的概念,它决定了代码的执行顺序和作用域链等重要信息。了解执行上下文的概念和工作原理,对于理解JavaScript的运行机制和调试错误非常有帮助。

在本文中,我们将深入探讨JavaScript的执行上下文,从而帮助读者更好地理解JavaScript的运行机制。

1、什么是执行上下文

一般来说,听到上下文这个东西,很自然想到了语文老师讲到的在上下文中找到相关联的段落和句子…

其实在JS中的上下文更多的是一个抽象的概念。它具体是指在当前执行环境中的变量、函数声明,参数(arguments),作用域链,this等信息

1.1、浏览器如何理解执行JavaScript

浏览器并不理解我们在应用中编写的高级JavaScript代码。代码需要被转换成浏览器和计算机能够理解的格式——机器码

浏览器在读取HTML时,如果遇到了<script> 标签或包含JavaScript代码的属性如onClick,会发送给JavaScript引擎

浏览器的JavaScript引擎会创造一个特殊的环境来处理这些JavaScript代码的转换和执行。这个特殊的环境被称为执行上下文

执行上下文包含当前正在运行的代码和有助于其执行的所有内容。在执行上下文运行期间,编译器解析代码,内存存储变量和函数,可执行的字节码生成后,代码执行。

实在不好理解,先入为主,将之想象成一个执行JS的容器

1.2、执行上下文

执行上下文JavaScript中非常重要的概念,它代表了代码执行时的环境。每当JavaScript引擎执行一段代码时,都会创建一个执行上下文。执行上下文包含了三个重要的组成部分:变量对象作用域链this值

  • 变量对象:是当前执行上下文中的变量、函数声明和函数参数的存储空间。
  • 作用域链:是当前执行上下文中所有父级执行上下文的变量对象的集合,它决定了当前执行上下文中变量的可访问性。
  • this值:代表当前函数的执行环境。

2、执行上下文有哪些类型呢

在这里插入图片描述

JavaScript中有三种执行上下文类型

  • 全局执行上下文(GEC)

    任何不在函数内部的代码都在全局上下文中。一个程序中只会有一个全局执行上下文

  • 函数执行上下文(FEC)

    每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,它在函数被调用时创建。函数上下文可以有任意多个。

  • Eval函数执行上下文

    执行在eval函数内部的代码也会有它属于自己的执行上下文。eval不经常被使用到。

    🏄🏻 小知识

    eval()函数计算JavaScript字符串,并把它作为脚本代码来执行。

    如果参数是一个表达式,eval()函数将执行表达式。如果参数是Javascript语句,eval()将执行Javascript语句。

主要的还是全局执行上下文函数执行上下文

3、执行上下文的生命周期

在JavaScript中,执行上下文的生命周期可以分为三个阶段:创建阶段、执行阶段和销毁阶段。

3.1、创建阶段

在创建阶段,执行上下文首先与执行上下文对象(ECO)相关联。执行上下文对象存储了许多重要的数据,执行上下文中的代码在运行时会使用这些数据。创建阶段分三个步骤来定义和设置执行上下文对象的属性:

  • 创建变量对象(VO
  • 创建作用域链
  • 设置this关键字的值

3.1.1、创建变量对象(VO)

变量对象(VO)是一个在执行上下文中创建的类似于对象的容器,存储执行上下文中变量函数声明

GEC中,每当使用var关键字声明变量,VO就会添加一个指向该变量的属性,并将值设置为undefined。每当函数声明时,VO就会添加一个指向该函数的属性,并将这个属性存储在内存中。这就意味着在开始运行代码之前,所有函数声明就已经存储在VO中,并可以在VO中访问

但在FEC中并不创建VO,而是生成一个类数组对象,称为arguments对象,在下文称AO,包含传入函数的所有参数。

🏄🏻 小知识

这种将变量和函数声明存储在内存中优先于执行代码的过程被称为提升

3.1.2、创建作用域链

JavaScript中的作用域链是一个机制,决定了一段代码对于代码库中其他一些代码来说的可访问性

可以带着这样一些问题思考:

  • 一段代码可以在哪里访问?哪里不能访问?
  • 代码哪些部分可以被访问?哪些部分不能?

每一个函数执行上下文都会创建一个作用域,作用域相当于是一个空间/环境,变量和函数定义在这个空间里,并且可以通过一个叫做作用域查找的过程访问。如果函数被定义在另一个函数内部,处在内部的函数可以访问自己内部的代码以及外部函数(父函数)的代码。这种行为被称作词法作用域查找。但外部函数并不能访问内部函数的代码。

🏄🏻 小知识

作用域的概念就引出了JavaScript另一个相关的现象——闭包。闭包指的是内部函数永远可以访问外部函数中的代码,即便外部函数已经执行完毕。

JavaScript引擎一路向上遍历执行上下文直至解析处在函数内部触发的变量和函数的概念就叫作用域链

3.1.3、设置this关键字的值

JavaScriptthis关键字指的是执行上下文所属的作用域。一旦作用域链被创建,JS引擎就会初始化this关键字的值。

全局上下文中的this值:

GEC(所有函数和对象之外)中,this指向全局对象——window对象。同时,由var关键字初始化的函数声明和变量会被作为全局对象(window对象)的方法或者属性。

在任何函数外声明的变量和函数,如下:

var name = "jack"; 

function getName() { 
  console.log('hello') 
};

与下方的写法是一致的:

window.name = "jack"; 

window.getName = () => { 
  console.log('hello') 
};

GEC中的函数和变量会被当作window对象的方法和属性。

函数中的this

FEC中,并没有创建this对象,而是能够访问this被定义的环境。

在函数内部访问this的属性,示例:

var msg = "hello world!"; 

function printMsg() { 
  console.log(this.msg); 
} 

printMsg(); // hello world!

🏄🏻 小知识

在对象中,this关键字并不指向GEC,而是指向对象本身。

引用对象中的this如同引用:

对象.定义在对象内部的属性或方法;

示例代码:

var msg = "hello world!"; 
const Obj = {
	msg = "no hello world!"; 
	printMsg() { console.log(this.msg); } 
}

Obj.printMsg(); // no hello world!

出现上述的情况,函数可以访问的this关键字的值是定义其的对象Obj,而不是全局对象。

this关键字的值设置后,执行上下文对象的所有属性就定义完成,创建阶段结束,JS引擎就进入到执行阶段。

3.2、执行阶段

执行上下文创建阶段之后就是执行阶段了,在这一阶段代码执行真正开始。创建阶段之后,VO包含的变量值为undefined,如果在此时运行代码,肯定会报错,因此JavaScript引擎无法执行未定义的变量。

在执行阶段,JavaScript引擎会再次读取执行上下文,并用变量的实际值更新VO。编译器再把代码编译为计算机可执行的字节码后执行。如果在代码执行过程中发生异常,JavaScript引擎会抛出异常并停止执行代码。

3.3、销毁阶段

执行上下文销毁阶段是指当一个函数执行完毕或者当前执行上下文被弹出执行上下文栈时,执行上下文会被销毁的过程。在执行上下文销毁阶段,JavaScript引擎会执行以下步骤:

  1. 垃圾回收JavaScript引擎会检查当前执行上下文中的变量对象和函数声明是否被其他对象引用。如果没有被引用,则这些对象将被标记为垃圾对象,并在垃圾回收过程中被清除。
  2. 变量销毁JavaScript引擎会销毁当前执行上下文中的所有变量。在函数执行结束时,所有局部变量将被销毁。在全局执行上下文中,全局变量只有在页面关闭时才会被销毁。
  3. 闭包变量销毁:如果当前执行上下文是一个闭包函数,那么其中的闭包变量将不会被销毁。这是因为闭包变量被外层函数的作用域链所引用,只有当外层函数被销毁时,闭包变量才会被销毁。
  4. 执行上下文弹出JavaScript引擎会将当前执行上下文从执行上下文栈中弹出,并将控制权返回给上一个执行上下文。

🏄🏻 小知识

ES5以上的规范,对于执行上下文的创建过程有所调整,移除了了ES3中的变量对象VO和活动对象AO,引入了词法环境组件LexicalEnvironment component) 和变量环境组件VariableEnvironment component)。

4、执行栈

执行栈又称调用栈,记录了脚本整个生命周期中生成的执行上下文。

🏄🏻 小知识

JavaScrip是单线程语言,也就是说它只能在同一时间执行一项任务。因此,其他的操作、函数和事件发生时,执行上下文也会被创建。由于单线程的特性,一个堆叠了执行上下文的栈就会被创建,称为执行栈

JS引擎会搜索代码中被调用的函数。每一次函数被调用,一个新的FEC就会被创建,并被放置在当前执行上下文的上方。而执行栈最顶部的执行上下文会成为活跃执行上下文,并且始终是JS引擎优先执行。

一旦活跃执行上下文中的代码被执行完毕,JS引擎就会从执行栈中弹出这个执行上下文,紧接着执行下一个执行上下文,以此类推。

4.1、示例代码

用一段代码来描述执行栈的流程

var name = "Guizimo";

function first() {
  var a = "Hi!";
  second();
  console.log(`${a} ${name}`);
}

function second() {
  var b = "Hey!";
  third();
  console.log(`${b} ${name}`);
}

function third() {
  var c = "Hello!";
  console.log(`${c} ${name}`);
}

first();

执行结果:

Hello! Guizimo
Hey! Guizimo
Hi! Guizimo

对于这个预料之中,但总感觉奇奇怪怪的结果…所以还是使用图示来讲解一下。

4.2、图示讲解

  1. JS引擎加载脚本,创建GEC,并压入执行栈的最底部。name变量,firstsecondthird函数在所有函数外部定义,所以位于GEC,并且被VO存储。

    image-20230726172038328

  2. JS引擎遇到first函数调用时,一个新的FEC被创建。新的执行上下文被放置在当前上下文上方,形成执行栈。在first函数调用时,其执行上下文变成活跃执行上下文。在first函数中的变量a ='Hi!'被存储在其FEC中,而非GEC中。

    image-20230726172250272

  3. 紧接着,second函数在first函数中被调用。由于JavaScript单线程的特性,first函数的执行会被暂停,直到second函数执行完闭,才会继续执行。同样的,JS引擎会给second函数设置一个新的FEC,并把它放置在栈顶端,并激活。second函数成为活跃执行上下文,变量b = 'Hey!'被存储在其FEC中。

    image-20230727073234049

  4. 再之后second函数中的third函数被调用,其FEC被创建并放置在执行栈的顶部。

    image-20230727073323502

  5. third函数中的变量c = 'Hello!'被存储在其FEC中,Hello! Guizimo在控制台中打印。等待third函数执行完毕后, 其FEC就从栈顶端弹出,而调用third函数的second函数重新成为活跃执行上下文。

    image-20230727073234049

  6. 回到second函数,控制台打印Hey! Guizimo。函数执行完成所有任务,这个执行上下文从执行栈上弹出。

    image-20230726172250272

  7. first函数执行完毕,从执行栈上弹出后,控制流回到代码的GEC

    image-20230726172038328

  8. 最终,所有代码执行完毕,JS引擎GEC从执行栈上弹出。

博客说明与致谢

文章所涉及的部分资料来自互联网整理,其中包含自己个人的总结和看法,分享的目的在于共建社区和巩固自己。

引用的资料如有侵权,请联系本人删除!

感谢勤劳的自己,个人博客,GitHub,公众号【归子莫】,小程序【子莫说】

如果你感觉对你有帮助的话,不妨给我点赞鼓励一下,好文记得收藏哟!

幸好我在,感谢你来!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/796255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue elemenet

日常开发当中&#xff0c;只使用vue是不够的&#xff0c;虽然提供给我们强大的功能&#xff0c;用的还是原生的html标签。 这可能就需要我们去关注前台美化展示的工作。 日常开发当中都是vue去配合前端的组件库&#xff0c;两个结合起来一起去开发。 知识点 Element Plus 介绍…

Qt应用开发(基础篇)——Button按钮

目录 一、前言 二&#xff1a;QAbstractButton 抽象按钮基类 三&#xff1a;QPushButton 普通按钮 四&#xff1a;QCommandLinkButton 链接按钮 五&#xff1a;QCheckBox 复选按钮 六&#xff1a;QRadioButton 单选按钮 七&#xff1a;QToolButton 工具按钮 一、前言 常…

一劳永逸的日报月报制作方法,还不快来get

许多政府机构、企业都会使用日报、月报来把握现有状况&#xff0c;比如说生产制造企业&#xff0c;需要通过日报来监控项目进度和生产数据。哪怕这些报表制作起来繁琐浪费、重复复杂&#xff0c;但是企业不得不通过日报对生产数据进行实时把控。 那么有没有一种办法或者工具&a…

Linux为何是软件开发专业人员的心头爱-Robotics Ubuntu

Linux与Windows都是十分常见的电脑操作系统&#xff0c;相信你对它们二者都有所了解&#xff01;在你的使用过程中&#xff0c;是否有什么事让你觉得在Linux上顺理成章&#xff0c;换到Windows上就令你费解&#xff1f;亦或者关于这二者你有任何想要分享的&#xff0c;都可以在…

幸福长寿的秘诀 —— 查理芒格

查理芒格&#xff1a;幸福长寿的秘诀其实很简单。_哔哩哔哩_bilibili People trying to figure out what the secret to life, is to a long and happy life ? Its simple. You dont have a lot of envy. You dont have a lot of resentment. You dont overspend your incom…

wms-3代货架标签系统(四个灯供电版本)接口文档

一、查询标签信息接口 接口类型&#xff1a;POST, 参数格式&#xff1a;json array 链接形式&#xff1a; http://localhost/wms/associate/getTagsMsg 代码形式&#xff1a; { url : http://localhost/wms/associate/getTagsMsg, requestMethed : GET, requestParamete…

Django模板语法和请求

1、在django关于模板文件加载顺序 创建的django项目下会有一个seeetings.py的文件 如果在seeetings.py 中加了 os.path.join(BASE_DIR,‘templates’)&#xff0c;如果是pycharm创建的django项目会加上&#xff0c;就会默认先去根目录找templates目录下的html文件&#xff0c…

CMIP6数据处理及在气候变化、水文、生态等领域中的实践技术应用

气候变化对农业、生态系统、社会经济以及人类的生存与发展具有深远影响&#xff0c;是当前全球关注的核心议题之一。IPCC&#xff08;Intergovernmental Panel on Climate Change&#xff0c;政府间气候变化专门委员会&#xff09;的第六次评估报告明确&#xff1b;指出&#x…

建议收藏:模拟版图面试题,含解析(附下载)

IC行业是一个充满竞争和机遇的领域&#xff0c;而作为一名模拟版图工程师&#xff0c;在面试中表现出色至关重要。 之前为大家全面解析过模拟版图&#xff0c;但面对面对即将找工作或者是面对今年秋招的的同学&#xff0c;可能对于模拟版图面试这块更感兴趣。 秋今天芯博士为…

Python数据可视化工具——Matplotlib

目录 1 基础准备1.1简介1.2安装1.3 绘图基础知识1.4 查询matplotlib系统中文字体 2 绘图流程2.1 简单绘图2.2 标准绘图2.3 绘制子图2.3.1 add_subplot方法两行一列子图 plt1.add_subplot(2,1,*)一行两列子图 plt1.add_subplot(1,2,*) 2.3.2 plt.subplot()方法 2.4 添加文字说明…

【必看,干货满满】K8S云原生技术小结

Docker容器技术总结&#xff1a; 1、Docker容器部署及常用命令详解 2、Dockerfile使用及案例详解 3、Docker容器数据卷详解 4、Docker网络详解 5、Docker资源配额详解 6、Docker私有仓库Harbor搭建及使用 7、Docker图形化管理工具Protainer搭建 8、Docker配置阿里镜像加速源&am…

最新版本docker 设置国内镜像源 加速办法

解决问题:加速 docker 设置国内镜像源 目录: 国内加速地址 修改方法 国内加速地址 1.Docker中国区官方镜像 https://registry.docker-cn.com 2.网易 http://hub-mirror.c.163.com 3.ustc https://docker.mirrors.ustc.edu.cn 4.中国科技大学 https://docker.mirrors…

【《React Hooks实战》——指导你使用hook开发性能优秀可复用性高的React组件】

使用React Hooks后&#xff0c;你很快就会发现&#xff0c;代码变得更具有组织性且更易于维护。React Hooks是旨在为用户提供跨组件的重用功能和共享功能的JavaScript函数。利用React Hooks&#xff0c; 可以将组件分成多个函数、管理状态和副作用&#xff0c;并且不必声明类即…

生信学院|07月27日《非线性仿真的领头羊之ABAQUS介绍》

课程主题&#xff1a;非线性仿真的领头羊之ABAQUS介绍 课程时间&#xff1a;2023年07月27日 14:00-14:30 主讲人&#xff1a;张明学 生信科技 CAE专家 ABAQUS功能ABAQUS应用Q&A 请安装腾讯会议客户端或APP&#xff0c;微信扫描海报中的二维码报名哦~~~ 或者点击链接报…

用js把地区字符串格式化为省、市、区

用js把地区信息格式化为省、市、详细信息&#xff0c;结果如下 代码如下 formatter("广东省深圳市南山区深南大道10000号") formatter("西藏自治区拉萨市城关区北京中路35号") formatter("四川省阿坝藏族羌族自治州九寨沟县漳扎镇301省道") for…

cass--单选不累加设置

打开软件&#xff0c;在空白处右击--选项--选择&#xff0c;如下&#xff1a; 完成后&#xff0c;点击确定按钮即可。

Vue学习Day3——生命周期\组件化

一、Vue生命周期 Vue生命周期&#xff1a;就是一个Vue实例从创建 到 销毁 的整个过程。 生命周期四个阶段&#xff1a;① 创建 ② 挂载 ③ 更新 ④ 销毁 1.创建阶段&#xff1a;创建响应式数据 2.挂载阶段&#xff1a;渲染模板 3.更新阶段&#xff1a;修改数据&#xff0c;更…

elment-ui的侧边栏 开关及窗口联动

<template><div class"asders"><el-aside width"200px"><div class"boxbody"><div>源码外卖</div><el-switch v-model"isCollapse" :active-value"true" :inactive-value"fals…

【U8+】财务三大UFO报表的勾稽关系

【三大报表】 资产负债表、利润表、现金流量表&#xff0c;称之为企业三大报表&#xff0c;也是最常见、常用的报表。 【勾稽关系】 核对报表的勾稽关系之前&#xff0c;需要保证“资产负债表”平衡&#xff0c;否则无法正确勾稽检查&#xff1b;资产负债表、利润表、现金流量…

护眼台灯哪个牌子好?三款主流品牌横向对比测评

随着暑假的到来&#xff0c;不少家长想添置或者换新的护眼台灯给孩子使用&#xff0c;护眼台灯正是线下一款炙手可热的护眼神器&#xff0c;很多家长纷纷想给自己孩子买一款真正护眼的台灯。不过面对市场上各种品牌和型号的护眼台灯&#xff0c;对于不熟悉或者是第一次购买护眼…