前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >了解ECMAScript规范,第1部分

了解ECMAScript规范,第1部分

作者头像
疯狂的技术宅
发布2020-02-26 14:59:09
7640
发布2020-02-26 14:59:09
举报
文章被收录于专栏:京程一灯京程一灯

每日前端夜话第280篇

翻译:疯狂的技术宅

作者:Marja Hölttä

来源:v8.dev

正文共:2388 字

预计阅读时间:10分钟

前言

即便你对 JavaScript 很了解,但是去阅读 ECMAScript 语言规范,或简称为 ECMAScript 规范(https://tc39.es/ecma262/)也会令人生畏。至少这是我在第一次开始阅读时的感受。

让我们从一个具体的例子开始,然后通过遍历整个规范以了解它。以下代码演示了 Object.prototype.hasOwnProperty 的用法:

代码语言:javascript
复制
const o = { foo: 1 };
o.hasOwnProperty('foo'); // true
o.hasOwnProperty('bar'); // false

在例子中,对象 o 没有名为 hasOwnProperty 的属性,让我们沿着原型链去寻找。可以在 o 的原型 Object.prototype 中找到它。

为了描述 Object.prototype.hasOwnProperty 的工作方式,该规范使用了类似伪代码的描述:

Object.prototype.hasOwnProperty(V) 当使用参数 V 调用 hasOwnProperty 方法时,将执行以下步骤:

  1. 使 P? ToPropertyKey(V)
  2. 使 O? ToObject(this value)
  3. 返回 ? HasOwnProperty(O, P)

HasOwnProperty(O, P) 抽象操作 HasOwnProperty 用于确定对象是否具有带有指定属性键的自己的属性。其返回一个布尔值。该操作使用参数 OP 调用,其中 O 是对象,而 P 是属性键。此抽象操作执行以下步骤:

  1. 断言:Type(O)Object
  2. 断言:IsPropertyKey(P)true
  3. 使 desc? O.[GetOwnProperty]
  4. 如果 descundefined,则返回 false
  5. 返回 true

但什么是“抽象操作”呢?[[]] 里面有什么东西?为什么在函数前面有一个? ?这些断言又是什么意思?

快来找出答案吧!

语言类型和规范类型

让我们从看上去熟悉的东西开始。规范使用了我们从 JavaScript 中已经知道的值,例如 undefinedtruefalse。它们都是 语言值(https://tc39.es/ecma262/#sec-ecmascript-language-types),即规范中所定义的 语言类型的值。

规范还在内部使用语言值,例如,内部数据类型可能包含一个字段,其可能值为 truefalse。相反,JavaScript 引擎通常在内部不使用语言值。例如,如果 JavaScript 引擎是用 C ++ 编写的,则通常会使用 C++ 的truefalse(而不是 JavaScript 的 truefalse 的内部表示)。

除语言类型外,规范还使用规格类型(https://tc39.es/ecma262/#sec-ecmascript-specification-types),这些类型仅在规范中出现,但不属于 JavaScript 语言。JavaScript 引擎不需要(但可以)实现它们。在本文中,我们将了解规范类型 Record(及其子类型 Completion Record)。

抽象操作

抽象操作(https://tc39.es/ecma262/#sec-abstract-operations)是 ECMAScript 规范中定义的函数;定义它们是为了简洁地编写规范。JavaScript 引擎不必将其作为单独的函数实现在引擎内部。不能从 JavaScript 直接调用它们。

内部插槽和内部方法

内部插槽内部方法(https://tc39.es/ecma262/#sec-object-internal-methods-and-internal-slots)使用包含在 [[]] 中的名称。

内部插槽是 JavaScript 对象或规范类型的数据成员。它们被用于存储对象的状态。内部方法是 JavaScript 对象的成员函数。

例如,每个 JavaScript 对象都有一个内部插槽 [[Prototype]] 和一个内部方法 [[GetOwnProperty]]

无法从 JavaScript 访问内部插槽和方法。例如,你无法访问 o.[[Prototype]] 或调用 o.[[GetOwnProperty]]()。JavaScript 引擎可以实现它们以供内部使用,但并不是必须的。

有时内部方法委托类似名称的抽象操作,例如在普通对象的 [[GetOwnProperty]] 中:

[[GetOwnProperty\]](P) 当使用属性键 P 调用 O[[GetOwnProperty]] 内部方法时,将执行以下步骤: 返回 ! OrdinaryGetOwnProperty(O,P)

(我们将在下一章中找到感叹号的含义。)

OrdinaryGetOwnProperty 不是内部方法,因为它没有与任何对象相关联;而是将对其进行操作的对象作为参数传递。

因为 OrdinaryGetOwnProperty 对普通对象起作用,所以被称为“普通”。ECMAScript 对象可以是普通外部的。普通对象必须具有一组被称为基本内部方法的方法的默认行为。如果某个对象偏离默认行为,则该对象是外部的。

最著名的外部对象是 Array,因为其 length 属性以非默认方式运行:设置 length 属性可以从 Array 中删除元素。

基本的内部方法是 https://tc39.es/ecma262/#table-5 中列出的方法。

完成记录(Completion records)

问号和感叹号是怎么回事?要了解它们,我们需要研究完成记录(Completion Records)(https://tc39.es/ecma262/#sec-completion-record-specification-type)!

完成记录是一种规范类型(仅出于规范目的而定义)。JavaScript 引擎不必具有相应的内部数据类型。

完成记录是一种“记录”——一种具有一组固定的命名字段的数据类型。完成记录包含三个字段:

代码语言:javascript
复制
normal, break, continue, return 或 throw 中的一个。除 normal 以外的所有其他类型都是突然完成”( abrupt completions)

每个抽象操作都隐式返回完成记录。即使看起来抽象操作会返回一个简单的类型(例如 Boolean),它也将被隐式包装为类型为 normal 的完成记录(请参见 隐式完成值(https://tc39.es/ecma262/#sec-implicit-completion-values)。

注1:规格在这方面并不完全一致;有些辅助函数返回裸值,并且其返回值按原样使用,而无需从“完成记录”中提取值。通常从上下文中可以清楚地看出这一点。

注2:规范制定人员正在研究使“完成记录”的处理更加明确。

如果算法抛出异常,则意味着返回带有 [[Type]] throw 的完成记录,而 [[Value]] 是异常对象。现在,我们将忽略 breakcontinuereturn 类型。

ReturnIfAbrupt(argument) 意味着采取以下步骤:

  1. 如果 argument 突然出现,则返回 argument
  2. argument 设置为 argument.[[Value]]

也就是说,我们检查完成记录;如果是突然完成,会立即返回。否则从完成记录中提取值。

ReturnIfAbrupt 可能看起来像一个函数调用,但事实并非如此。它是导致返回 ReturnIfAbrupt() 的函数返回的原因,而不是返回 ReturnIfAbrupt 函数本身的函数。它的行为更像是 C 语言中的宏。

ReturnIfAbrupt 可以这样使用:

  1. objFoo()。(obj 是 Completion Record。)
  2. ReturnIfAbrupt(obj)
  3. Bar(obj). (如果仍在此处,则 obj 是从 Completion Record 中提取的值)

现在问号(https://tc39.es/ecma262/#sec-returnifabrupt-shorthands)开始起作用:? Foo() 等同于 ReturnIfAbrupt(Foo())。使用简写很实用:我们不必每次都明确编写错误处理代码。

同样, Let val be ! Foo() 等效于:

  1. 使 valFoo()
  2. 断言:val 不是突然完成的
  3. val 设置为 val.[[Value]]

利用这些知识,我们可以这样重写 Object.prototype.hasOwnProperty

Object.prototype.hasOwnProperty(P)

  1. 使 PToPropertyKey(V)
  2. 如果 P 是突然完成的,则返回 P
  3. P 设置为 P.[[Value]]
  4. OToObject(this value)
  5. 如果 O 是突然完成的,则返回 O
  6. O 设置为 O.[[Value]]
  7. 使 tempHasOwnProperty(O, P)
  8. 如果temp是突然完成的,则返回temp
  9. 使 temptemp.[[Value]]
  10. 返回 NormalCompletion(temp)

…我们可以这样重写 HasOwnProperty

HasOwnProperty(O, P)

  1. 断言:Type(O)Object
  2. 断言:IsPropertyKey(P)true.
  3. descO.[GetOwnProperty]
  4. 如果 desc 是突然完成,则返回 desc
  5. desc 设置为 desc.[[Value]]
  6. 如果 descundefined, 则返回 NormalCompletion(false)
  7. 返回 NormalCompletion(true)

我们也可以重写不带感叹号的 [[GetOwnProperty]] 内部方法:

O.[[GetOwnProperty]]

  1. tempOrdinaryGetOwnProperty(O, P)
  2. 断言:temp 不是突然完成
  3. 使 temptemp.[[Value]]
  4. 返回 NormalCompletion(temp)

在这里,我们假设 temp 是一个全新的临时变量,不会与其他任何冲突。

我们还使用了以下知识:当 return 语句返回除 Completion Record 以外的其他内容时,它隐式包装在 NormalCompletion 中。

Return ? Foo()

规范使用 Return ? Foo() ——为什么有问号?

Return ? Foo() 扩展为:

  1. 使 tempFoo()
  2. 如果temp是突然完成的,则返回temp
  3. temp 设置为 temp.[[Value]]
  4. 返回 NormalCompletion(temp)

Return Foo() 相同;对于突然和正常完成,其行为方式相同。

Return ? Foo() 仅出于编辑原因而使用,以使其能够更明确地表达 Foo 返回完成记录。

断言

规范中的主张断言了算法的不变条件。为了清楚起见,添加了它们,但没有对实现添加任何要求——实现中不需要检查它们。

继续

我们已经建立了阅读规范所需的知识,如 Object.prototype.hasOwnProperty 之类的简单方法和诸如 HasOwnProperty 之类的抽象操作。它们仍然委托其他抽象操作,但是基于本文,我们应该能够弄清楚它们的作用。我们将遇到属性描述符,这是另一种规范类型。

从 Object.prototype.hasOwnProperty 开始的函数调用图

有用的链接

如何阅读 ECMAScript 规范(https://timothygu.me/es-howto/):该教程从一个稍微不同的角度涵盖了本文中的许多内容。

原文链接

https://v8.dev/blog/understanding-ecmascript-part-1

2020年京程一灯全新课程体系即将推出,请保持关注。

愿你在新的一年里保持技术领先,有个好前程,愿你月薪30K。我们是认真的 !

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端先锋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 语言类型和规范类型
  • 抽象操作
  • 内部插槽和内部方法
  • 完成记录(Completion records)
    • Return ? Foo()
    • 断言
    • 继续
    • 有用的链接
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档