谁知道try里面放return,finally还会执行吗?

news2024/12/26 0:49:23

在前面几篇文章中,我们已经了解了关于执行上下文、作用域、闭包之间的关系。

今天,我们则要说一说更为细节的部分:语句。

语句是任何编程语言的基础结构,与 JavaScript 对象一样,JavaScript 语句同样具有“看起来很像其它语言,但是其实一点都不一样”的特点。

我们比较常见的语句包括变量声明、表达式、条件、循环等,这些都是大家非常熟悉的东西,对于它们的行为,我在这里就不赘述了。

为了了解 JavaScript 语句有哪些特别之处,首先我们要看一个不太常见的例子,我会通过这个例子,来向你介绍 JavaScript 语句执行机制涉及的一种基础类型:Completion 类型。

Completion 类型

我们来看一个例子。在函数 foo 中,使用了一组 try 语句。我们可以先来做一个小实验,在 try 中有 return 语句,finally 中的内容还会执行吗?我们来看一段代码。

function foo(){
  try{
    return 0;
  } catch(err) {
  } finally {
    console.log("a")
  }
}
console.log(foo());

 通过实际试验,我们可以看到,finally 确实执行了,而且 return 语句也生效了,foo() 返回了结果 0。

虽然 return 执行了,但是函数并没有立即返回,又执行了 finally 里面的内容,这样的行为违背了很多人的直觉。

如果在这个例子中,我们在 finally 中加入 return 语句,会发生什么呢?

function foo(){
  try{
    return 0;
  } catch(err) {
  } finally {
    return 1;
  }
}
console.log(foo());

通过实际执行,我们看到,finally 中的 return “覆盖”了 try 中的 return。在一个函数中执行了两次 return,这已经超出了很多人的常识,也是其它语言中不会出现的一种行为。

面对如此怪异的行为,我们当然可以把它作为一个孤立的知识去记忆,但是实际上,这背后有一套机制在运作。

这一机制的基础正是 JavaScript 语句执行的完成状态,我们用一个标准类型来表示:Completion Record(我在类型一节提到过,Completion Record 用于描述异常、跳出等语句执行过程)。

Completion Record 表示一个语句执行完之后的结果,它有三个字段:

  • [type] 表示完成的类型,有 break continue return throw 和 normal 几种类型;

  • [value] 表示语句的返回值,如果语句没有,则是 empty;

  • [target] 表示语句的目标,通常是一个 JavaScript 标签(标签在后文会有介绍)。

JavaScript 正是依靠语句的 Completion Record 类型,方才可以在语句的复杂嵌套结构中,实现各种控制。接下来我们要来了解一下 JavaScript 使用 Completion Record 类型,控制语句执行的过程。

首先我们来看看语句有几种分类。

普通的语句

在 JavaScript 中,我们把不带控制能力的语句称为普通语句。普通语句有下面几种。

  • 声明类语句

    • var 声明

    • const 声明

    • let 声明

    • 函数声明

    • 类声明

  • 表达式语句

  • 空语句

  • debugger 语句

这些语句在执行时,从前到后顺次执行(我们这里先忽略 var 和函数声明的预处理机制),没有任何分支或者重复执行逻辑。

普通语句执行后,会得到 [type] 为 normal 的 Completion Record,JavaScript 引擎遇到这样的 Completion Record,会继续执行下一条语句。

这些语句中,只有表达式语句会产生 [value],当然,从引擎控制的角度,这个 value 并没有什么用处。

如果你经常使用 Chrome 自带的调试工具,可以知道,输入一个表达式,在控制台可以得到结果,但是在前面加上 var,就变成了 undefined。

Chrome 控制台显示的正是语句的 Completion Record 的[value]。

语句块

介绍完了普通语句,我们再来介绍一个比较特殊的语句:语句块。

语句块就是拿大括号括起来的一组语句,它是一种语句的复合结构,可以嵌套。

语句块本身并不复杂,我们需要注意的是语句块内部的语句的 Completion Record 的[type] 如果不为 normal,会打断语句块后续的语句执行。

比如我们考虑,一个[type]为 return 的语句,出现在一个语句块中的情况。

从语句的这个 type 中,我们大概可以猜到它由哪些特定语句产生,我们就来说说最开始的例子中的 return。

return 语句可能产生 return 或者 throw 类型的 Completion Record。我们来看一个例子。

先给出一个内部为普通语句的语句块:

{
  var i = 1; // normal, empty, empty
  i ++; // normal, 1, empty
  console.log(i) //normal, undefined, empty
} // normal, undefined, empty

在每一行的注释中,我给出了语句的 Completion Record。

我们看到,在一个 block 中,如果每一个语句都是 normal 类型,那么它会顺次执行。接下来我们加入 return 试试看。

{
  var i = 1; // normal, empty, empty
  return i; // return, 1, empty
  i ++; 
  console.log(i)
} // return, 1, empty

但是假如我们在 block 中插入了一条 return 语句,产生了一个非 normal 记录,那么整个 block 会成为非 normal。这个结构就保证了非 normal 的完成类型可以穿透复杂的语句嵌套结构,产生控制效果。

接下来我们就具体讲讲控制类语句。

控制型语句

控制型语句带有 if、switch 关键字,它们会对不同类型的 Completion Record 产生反应。

控制类语句分成两部分,一类是对其内部造成影响,如 if、switch、while/for、try。

另一类是对外部造成影响如 break、continue、return、throw,这两类语句的配合,会产生控制代码执行顺序和执行逻辑的效果,这也是我们编程的主要工作。

一般来说, for/while - break/continue 和 try - throw 这样比较符合逻辑的组合,是大家比较熟悉的,但是,实际上,我们需要控制语句跟 break 、continue 、return 、throw 四种类型与控制语句两两组合产生的效果。

通过这个表,我们不难发现知识的盲点,也就是我们最初的的 case 中的 try 和 return 的组合了。

因为 finally 中的内容必须保证执行,所以 try/catch 执行完毕,即使得到的结果是非 normal 型的完成记录,也必须要执行 finally。

而当 finally 执行也得到了非 normal 记录,则会使 finally 中的记录作为整个 try 结构的结果。

带标签的语句

前文我重点讲了 type 在语句控制中的作用,接下来我们重点来讲一下最后一个字段:target,这涉及了 JavaScript 中的一个语法,带标签的语句。

实际上,任何 JavaScript 语句是可以加标签的,在语句前加冒号即可:

firstStatement: var i = 1;

大部分时候,这个东西类似于注释,没有任何用处。唯一有作用的时候是:与完成记录类型中的 target 相配合,用于跳出多层循环。

outer: while(true) {
    inner: while(true) {
        break outer;
    }
}
console.log("finished")

break/continue 语句如果后跟了关键字,会产生带 target 的完成记录。一旦完成记录带了 target,那么只有拥有对应 label 的循环语句会消费它。

结语

我们以 Completion Record 类型为线索,为你讲解了 JavaScript 语句执行的原理。

因为 JavaScript 语句存在着嵌套关系,所以执行过程实际上主要在一个树形结构上进行, 树形结构的每一个节点执行后产生 Completion Record,根据语句的结构和 Completion Record,JavaScript 实现了各种分支和跳出逻辑。

更多精彩👉:开发者网站--技术前沿

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

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

相关文章

孤儿进程与僵尸进程以及僵尸进程的解决

孤儿进程: 定义: 父进程运行结束,但子进程还在运行(未运行结束),这样的子进程就称为孤儿进程( Orphan Process )。 过程: 每当出现一个孤儿进程的时候,内核就…

放空一下自我 free

文章目录 放空一下自我 free默认的效果使用易读的参数间隔显示内存状态查看meminfo文件更多信息 放空一下自我 free **free**这个命令在Linux系统监控的工具里面,算是使用的比较多的一个。 使用_man_查看可知,官方含义为: Display amount o…

Dockerfile: CMD与ENTRYPOINT区别

CMD和ENTRYPOINT的作用 CMD和ENTRYPOINT这两个命令,我接触到的是用在了Dockerfile中用于构建容器。 CMD:The main purpose of a CMD is to provide defaults for an executing container. CMD的主要用途是为正在执行的容器提供默认值。也就是指定这个容…

D课堂 | 为什么网站搭建好了却无法访问?(上)

在上一期D课堂中,D妹教大家如何用最简单的方法快速搭建一个网站,相信很多小伙伴已经跃跃欲试,尝试去搭建自己的网站。(点击这里可以快速复习) 然而,有不少人明明每个步骤都跟着做了,但最后在浏览…

企业销售获客难?分享一个精准筛查企业客户的技巧

作为企业销售经理,曾经一直让我们很头疼的问题之一就是获客困难。回想起以往,我们需要通过各种手段,手动查找电话名单、网络搜索到各种渠道,费尽心思的去筛查才能找到潜在客户。获客流程长还耗费很多的精力,拿到手的客…

【test】wsl2和win互ping

参考: https://zhuanlan.zhihu.com/p/365058237 https://blog.csdn.net/Cypher_X/article/details/123011200

RTKlib操作手册--使用样例数据演示

简介 RTKLIB(Real-Time Kinematic Library)是一款开源的实时差分全球导航卫星系统(GNSS)软件库。它旨在提供高精度的位置解算,特别是在实时应用中,如精密农业、测绘、无人机导航等领域。 RTKLIB支持多种G…

复杂经济时期下的企业财务规划战略

多重危机、通货膨胀、外汇波动和市场变化的交汇给经济世界带来了前所未有的挑战,这使得企业对预测精准度和及时性的需求大大增加。平衡营收增长与成本输出的稳定性、在不断变化的市场行为中抓住商机提高盈利能力是现阶段财务专业人士必须掌握的技能。情景规划与财务…

Ubuntu下,Flutter安装及在VScode中的配置

1、安装flutter 在自己指定的目录下,新建文件夹,并将源码git clone到本地 $ mkdir flutter $ cd flutter$ git clone -b master https://github.com/flutter/flutter.git2、给flutter添加环境变量 #编辑配置文件 $ vi ~/.bashrc #在末尾加入以下内容&…

分布式系统中的CAP原理

分布式系统中的CAP原理 本文已收录至我的个人网站:程序员波特,主要记录Java相关技术系列教程,共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源,让想要学习的你,不再迷茫。 简介 在分布式系统中&…

rtklib读取原始数据是一次读取了一个文件的全部数据

一般来说,rtklib读取观测值文件(o文件)和导航文件(n文件)进行解算。 读取文件的时候,并非一次读取一个历元,而是将一个文件所有历元的数据都读取完毕以后,再进行解算。 这看起来是…

Go 如何处理死锁以提供哪些工具来检测或防死锁?

并发是 Go 的核心特性,它使程序能够同时处理多个任务。它是现代编程的一个强大组件,如果使用正确,可以产生高效、高性能的应用程序。然而,并发性也带来了顺序编程中不存在的某些类型错误的可能性,其中最臭名昭著的是死…

一般大家怎么部署java项目,要不要部署在docker里?

关于是否应该将Java项目部署在Docker中的思考 传统方式:直接在服务器上运行jar包,依赖于服务器的环境配置,可能会遇到环境不一致的问题。 Docker方式:通过容器化,你的应用和所有依赖都封装在一个Docker镜像中。部署时…

Portal认证

目录 一、Portal认证概述 1、802.1X和Portal对比 2、Portal认证流程 (1)、portal认证基本流程 二、MAC认证 1、MAC认证需求 2、MAC认证概述 3、MAC旁路认证 一、Portal认证概述 1、802.1X和Portal对比 802.1X认证要求认证终端安装专门的软件 8…

高效工作法:占位图片生成工具助力项目快速迭代

在现代设计和开发项目中,图片资源的重要性不言而喻。然而,项目中经常会遇到寻找合适图片、调整图片尺寸和格式等问题,这些问题不仅耗时耗力,还可能影响到项目的进度和质量。此时,占位图片生成工具应运而生,…

检索增强生成RAG

文章目录 RAG解释混合检索重排序Rerank为什么需要RerankHNSW带来的随机性问题 当前大模型处理长输入的水平依然不够大模型如何处理长输入?重要信息位置为什么会影响大模型的效果LangChain的解决方案-检索后重新排序文档 召回模式N选1召回模式多路召回模式 摘要 在RA…

黑马苍穹外卖学习Day6

HttpClient 介绍 HttpClient 是 Apache 提供的一个开源的 Java HTTP 客户端库,用于发送 HTTP 请求和处理 HTTP 响应。它提供了一种更简便的方式来执行 HTTP 请求,并支持多种协议,如 HTTP、HTTPS、FTP 等。 使用 HttpClient 可以方便地与远程…

Linux网络服务部署yum仓库

目录 一、网络文件 1.1.存储类型 1.2.FTP 文件传输协议 1.3.传输模式 二、内网搭建yum仓库 一、网络文件 1.1.存储类型 直连式存储:Direct-Attached Storage,简称DAS 存储区域网络:Storage Area Network,简称SAN&#xff0…

多级缓存架构(五)缓存同步

文章目录 一、Canal服务1. mysql添加canal用户2. mysql配置文件3. canal配置文件 二、引入依赖三、监听Canal消息四、运行五、测试 通过本文章,可以完成多级缓存架构中的缓存同步。 一、Canal服务 1. mysql添加canal用户 连接在上一次multiCache项目中运行的mys…

从传统训练到预训练和微调的训练策略

目录 前言1 使用基础模型训练手段的传统训练策略1.1 随机初始化为模型提供初始点1.2 目标函数设定是优化性能的关键 2 BERT微调策略: 适应具体任务的精妙调整2.1 利用不同的representation和分类器进行微调2.2 通过fine-tuning适应具体任务 3 T5预训练策略: 统一任务形式以提高…