前言
ECMAScript标准是深入学习JavaScript原理最好的资料,没有其二。
通过增加对ECMAScript语言的理解,理解javascript现象后面的逻辑,提升个人编码能力。
欢迎关注和订阅专栏 重学前端-ECMAScript协议上篇
阅读建议
- 了解 存在 语言类型 和规范类型
- 知道 引用记录和环境记录的概念
- 了解 完成记录
对应协议 第六章 Data Types and Values , 语言类型和规范类型。
语言类型即编程中常用的数据类型。
规范类型值可用于描述 ECMAScript 表达式计算的中间结果,但这些值不能作为对象的属性或 ECMAScript 语言变量的值存储。
ECMAScript 语言类型
就是Javascript 编程中常用的数据类型。
ECMAScript 语言类型包括 Undefined, Null, Boolean, String, Symbol, Number, BigInt, and Object。
有人会问,为什么是大写。 列个表格就清晰了
typof 操作符 呢,是取其值判断后返回一个值给开发者用的,这里面当然就有经典 null返回 obejct。
类型 | 备注 | typeof 的值 |
---|---|---|
Undefined Type | 只有一个值,undefined | "undefined" |
Null Type | 只有一个值,null | "object" |
Boolean Type | 两个值, true 和 false | "boolean" |
String Type | "string" | |
The Symbol Type | "symbol" | |
Numeric Types | Number and BigInt | "number" |
The Object Type | "object" |
ECMAScript规范类型
它们不一定对应于 ECMAScript 实现中的任何特定实体。规范类型值可用于描述 ECMAScript 表达式计算的中间结果,但这些值不能作为对象的属性或 ECMAScript 语言变量的值存储。
这些规范类型,你大概有个印象即可,当协议中出现,你大致能明白是什么意思即可。
必须了解 引用记录 类型,这会面会有单独的章节讲述:
规范类型表格
英文名 | 中文名 | 备注 |
---|---|---|
Enum Specification Type | 枚举类型 | 枚举是规范内部的值,不能从 ECMAScript 代码直接观察到。主要是为了清晰、精确地定义和描述规范中涉及的特定状态或类型。例如,完成记录Completion Record的[[Type]]字段可以取NORMAL、RETURN或THROW等值。 |
List and Record Specification Types | 列表和记录 | List Type:参数列表。用于在新表达式、函数调用和其他需要简单有序值列表的算法中解释参数列表。函数的 arguments 就是这玩意。 Record type:描述本规范算法中的数据聚合。 Record 类型值由一个或多个命名字段组成。每个字段的值都是 ECMAScript 语言值或规范值。字段名总是用双括号括起来,例如[[ Value ]]。类似typescript的Record。 |
Set and Relation Specification Types | 集合与关系规范类型 | 集合(Set)类型用于在内存模型中解释无序元素的集合。它与ECMAScript中同名的集合类型是不同的。为了避免混淆,在本规范中,ECMAScript集合类型的实例始终被称为"Set对象"。Set类型值是元素的简单集合,其中任何元素都不重复出现。可以向集合中添加或移除元素。集合之间可以进行合并(联合)、交集或差集运算。简言之,这里讨论的"Set类型"是一种理论上的数据结构概念,用于规范文档内部的说明和理论模型,强调的是数学意义上的无序唯一元素集合。而"Set对象"则是ECMAScript语言中实际可使用的、具备相应API的内置对象,如添加元素(add)、删除元素(delete)、检查元素是否存在(has)等方法,以及集合的基本操作如并集、交集等,这些都是通过具体的方法调用来实现的。 Relation Specification Type: 用来描述对集合元素之间某种特定约束或联系的抽象概念。关系类型的值是由其值域中的值组成的有序对集合。例如,一个关于事件的关系就是一个事件的有序对集合。对于关系R和R值域中的两个值a和b,a R b是表示有序对(a, b)是R的一个成员的简写方式。 |
Completion Record Specification Type | 完成记录 | 用于解释值和控制流的运行,例如执行控制的非本地传输的语句(break、continue、return和throw)的行为。 |
Reference Record Specification Type | 引用记录 |
引用记录类型用于解释诸如delete、typeof、赋值运算符、super关键字和其他语言特性等运算符的行为。例如,在执行obj.prop = value ;这样的赋值操作时,obj.prop就会生成一个引用记录,引用名称是prop。 |
Property Descriptor Specification Type | 属性描述符 | 解释对象属性属性的操作和具体化。对应这开发者常说的数据属性描述符和访问器属性描述符。属性描述符是访问器类型,它应包含[[Get]]、[[Set]]、[[Enumerable]]和[[Configurable]]字段;如果是数据类型,则应包含[[Value]]、[[Writable]]、[[Enumerable]]和[[Configurable]]字段。 |
Environment Record Specification Type | 环境记录 | 用于解释嵌套函数和块中的标志符解析行为。环境记录是ECMAScript引擎内部用于跟踪变量和函数绑定的一种机制,它维护了当前执行上下文中变量名与变量值之间的映射关系。 在涉及到作用域链、闭包等概念时,环境记录扮演了关键角色。在函数嵌套或代码块嵌套的情况下,环境记录会形成一个层次结构,确保正确的变量查找和生命周期管理。 |
Abstract Closure Specification Type | 抽象闭包 | 一系列算法步骤及其相关联的值集合。抽象闭包是一种元值,可以通过类似于函数调用的方式来调用,例如 closure(arg1, arg2) 。与抽象操作类似,调用抽象闭包会执行闭包描述的算法步骤。 |
Data Blocks | 数据块 | 用于描述字节大小(8位)的数值的独特且可变的序列。字节值是0到255之间的包含区间内的整数。创建一个 Data Block 值,该值具有固定数量的字节,每个字节的初始值为0。驻留在内存中且可以从多个代理(agent)并发引用的数据块被指定为共享数据块(Shared Data Block)。 |
PrivateElement Specification Type | 私有字段/方法/访问器 | 用于描述私有类字段、方法和访问器的一种Record类型。尽管不使用属性描述符(Property Descriptors)来直接定义私有元素,但私有字段的行为类似于不可配置、不可枚举、可写的数据属性,私有方法的行为类似于不可配置、不可枚举、不可写的(函数)数据属性,而私有访问器的行为则类似于不可配置、不可枚举的访问器属性 |
ClassFieldDefinition Record Specification Type | 类字段定义类型 | 用于规范类的字段。类字段可以是数据属性(Data Properties)或访问器属性(Accessor Properties)。 |
Private Names | 私有名称 | 用于描述一个全局唯一的值,这个值代表着类私有元素(字段、方法或访问器)的键。由于Private Name 的全局唯一性,它能确保类内部的私有成员不会在类外部被直接访问或修改,从而增强代码的安全性和封装性。 |
ClassStaticBlockDefinition Record Specification Type | class静态初始化块. | 用于封装类静态初始化块的可执行代码。具体看如下代码段 |
静态块:
vbnet
js
复制代码
class MyClass {
static {
console.log('Static initialization block');
MyClass.staticProp = {};
}
}
前面提到了 Enum Specification Type 类似 TypeScript enum 类型, 简单了解一下 typescripe enum的实现原理。
ini
javascript
复制代码
enum EnumCompletionRecordType {
normal = 0,
break = 1,
continue = 3,
return = 4,
throw = 5
}
使用 tsc 转换为js代码
javascript
javascript
复制代码
"use strict";
var EnumCompletionRecordType;
(function (EnumCompletionRecordType) {
EnumCompletionRecordType[EnumCompletionRecordType["normal"] = 0] = "normal";
EnumCompletionRecordType[EnumCompletionRecordType["break"] = 1] = "break";
EnumCompletionRecordType[EnumCompletionRecordType["continue"] = 3] = "continue";
EnumCompletionRecordType[EnumCompletionRecordType["return"] = 4] = "return";
EnumCompletionRecordType[EnumCompletionRecordType["throw"] = 5] = "throw";
})(EnumCompletionRecordType || (EnumCompletionRecordType = {}));
再输出一下 EnumCompletionRecordType 最后值的结构

发生ts的枚举类型最后被转为一个对象, 有键和值的对应,也有值和键的对应关系。
符合ECMAScript的实现必须提供并支持本规范中描述的所有类型、值、对象、属性、函数以及程序的语法和语义。
Enum Specification Type
枚举类型。枚举(Enums)是规范内部的值,不能直接从ECMAScript代码中观察到。
在协议中都是枚举值都是大写字母。
比如完成记录的 [[Type]]
字段

比如私有字段的 [[Kind]]
字段

List and Record Specification Types
列表(List)类型用于说明在新建表达式、函数调用以及其它需要简单有序值序列的算法中,参数列表(参见13.3.8节)的求值过程。List类型值就是由各个独立值组成的简单有序序列。这些序列的长度可以任意。列表的元素可以通过0起始的索引随机访问。为了记号上的方便,可以使用类似数组的语法来访问List的元素。例如,arguments[2] 是说列表arguments中第3个元素的一种简写方式。
这是不是就是类数组了。
表示法:例如,<< 1, 2 >> 定义了一个列表值,它包含两个元素,每个元素都被初始化为特定的值。一个新空列表可以表示为 << >>.
例如对象转原始值描述:

记录(Record)类型用于描述本规范中算法内部的数据聚合。记录类型值由一个或多个命名字段组成。每个字段的值可以是ECMAScript语言值或规范值。字段名总是用双中括号包围,例如 [[Value]] 。
这意味着,在规范文档中,当提到诸如[[Value]]这样的表达时,它指的是记录类型中的一个特定字段,用于存储某种特定的信息或数据。这类表示法常用于ECMAScript规范文档中,以标准化和精确地描述语言内部的结构和操作,尽管在实际编写JavaScript代码时,我们并不会直接使用这样的双中括号语法来定义对象的属性。
其实这个和 TypeScript 的 Record 类型很相似,只不过
- 用于表示协议的数据
- 字段名总是用双中括号包围,例如 [[Value]] 。
比如函数对象

又比如完成记录:

Completion Record Specification Type
完成记录(Completion Record)被用来解释值在运行时的传播以及控制流的转移,例如那些执行非局部控制转移的语句(如break
、continue
、return
和throw
)的行为。
大概的数据结构如下:

字段名 | 值 | 含义 |
---|---|---|
[[Type]] | NORMAL、BREAK、CONTINUE、RETURN 或 THROW | 发生的完成类型 |
[[Value]] | 任何值,除了完成记录 | 产生的值 |
[[Target]] | 一个字符串或空 | 有向控制转移的目标标签。 |
[[Target]]
有值的时候,一般出现在出现 label 的时候,比如
css
javascript
复制代码
label: for (let i = 0; i < 10; i++) {
if (i === 5) {
break label1; // 跳出循环,跳转到 label
}
console.log(i);
}
以下是一些用于指代完成记录的简写术语:
英文名 | 中文 | 说明 |
---|---|---|
normal completion | 正常完成 | 指的是任何[[Type]] 值为normal 的完成记录。当函数执行到代码末尾而没有遇到return 语句或其他中断执行的控制流语句。 |
break completion | 中断完成 | 指的是任何[[Type]] 值为break 的完成记录。即break语句返回的完成记录。 |
continue completion | 继续完成 | 指的是任何[[Type]] 值为continue 的完成记录。即continue语句返回的完成记录。 |
return completion | 返回完成 | 指的是任何[[Type]] 值为return 的完成记录。即 return语句返回的完成记录。 |
throw completion | 抛出完成 | 指的是任何[[Type]] 值为throw 的完成记录。即throw语句返回的完成记录。 |
还有两种特别的完成记录术语:
英文名 | 中文 | 说明 |
---|---|---|
abrupt completion | 突然完成 | 指的是任何[[Type]] 值不是normal 的完成记录。即 break completion,continue completion, return completion, throw completion中的一种。 |
a normal completion containing some type of value | 包含某类型值的正常完成 | 指的是在其[[Value]] 字段中具有该类型值的正常完成。 |
这里接下来要说的是 a normal completion containing some type of value 和 normal completion。
协议里有一段话,有红色标记的话,这里的 callable objects 指的就是函数对象,说 callable objects 在本协议中,只能返回 normal completion 或者 throw completion。

那这就很意思了,函数最后可以return 啊, 为什么不可以返回 return completion呢?
查看 return 语句 ReturnStatement执行, 其返回的确实是 [[Type]]
为 RETURN的完成记录。

可还记得,是怎么识别或者区分是不是 callable objects的呢? 就是检查有没有 [[Call]]
方法,实际调用也会执行这个 [[Call]]
方法

再一起看看 callable objects 即 函数对象的 这个[[Call]]

其返回的是 either a normal completion containing an ECMAScript language value or a throw completion 。
所以这里返回 normal completion containing an ECMAScript language value
- 并不是 [[Type]] 为 NORMAL 的 normal completion,
- 而是
[[Value]]
字段中具有某类型值的 normal completion containing some type of value, 其[[Type]]
不限于 NORMAL。
是不是有点绕,但这么理解是更为合理的。