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

理解 ECMAScript 规范(一)

作者头像
三毛
发布2020-03-05 17:31:41
5770
发布2020-03-05 17:31:41
举报
文章被收录于专栏:漫漫前端路

此文译自:v8.dev/blog/unders…

在这篇文章中,为我们将会使用规范中的一个小的功能来做为切入点,从中去尝试理解一些特殊操作符,让我们开始吧。

前言

即使你了解 JavaScript,阅读它的规范(ECMAScript 语言规范简称 ECMAScript 规范) 也是会令人生畏的。至少这是我第一次阅读时的感受。

让我们通过一个具体的示例开始。下面的代码演示了 Object.prototype.hasOwnProperty 的使用:

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

在这个例子中,o 没有 hasOwnProperty 属性,因此我们会在它的原型链上寻找 hasOwnProperty 属性,最终我们在 Object.prototypeo 的原型上找到了它。

为了描述 Object.prototype.hasOwnProperty 是怎么工作的,ECMAScript 规范使用了伪代码来描述它:

Object.prototype.hasOwnProperty(v) 当带参数 v 调用 hasOwnProperty,会经过以下步骤 1、令 P?ToPropertyKey(V). 2、令 O?ToObject(this value). 3、返回 ?HasOwnProperty(O, P).

以及:

HasOwnProperty 抽象的操作符 HasOwnProperty 用来确认一个对象自身是否含有某个特定键的属性,它返回有一个布尔值。调用这个操作符需要两个参数,OP,前者是一个对象,后者是个属性键的名称。这个抽象操作符的执行,会经过以下步骤: 1、断言:Type(O) 是一个对象 2、断言: IsPropertyKey(P) 为 true 3、令 desc?O.[[GetOwnProperty]](P) 4、如果 descundefined,返回 false 5、返回 true

但是什么是一个「抽象操作符」?[[ ]] 里面的内容是什么?为什么在一个函数之前有一个 ?,这个「推断」又是什么意思呢?

现在,让我们找到答案!

语言类型和规范类型

让我们先来看一对相似的概念,语言类型和规范类型。ECMAScript 规范使用 undefinedtruefalse,等值 —— 我们已从 JavaScript 了解它们。它们都是「语言值」,也就是规范中定义的语言类型的值。

在规范中也使用了语言内置的值,比如一个内置的数据类型可能包含一个值为 truefalse 的字段,相反,JavaScript 引擎通常不会使用这些内置的语言值。例如,如果 JavScript 引擎是使用 C++ 编写,它可能会使用 c++ 里面的 truefalse,而不是 JavaScript 内部的 truefalse

除了语言类型外,规范中还使用了规范类型,它们是仅仅出现在规范中而没有出现在 JavaScript 中的类型。JavaScript 引擎不必(当然也可以)实现它们。在这篇文章中,我们将会去了解其中一个规范类型 -- Record 以及它的子类型 Completion Record

抽象操作符

抽象操作符是规范中定义的函数,定义它们是为了更加简洁的书写规范。JavaScript 引擎也不必把它们做为一个单独的内部函数来实现它们。在 JavaScript 中,它们不能被调用。

内部的插槽和函数

为了更好的书写规范,内部的插槽和函数被定义,它们通常被包裹在 [[]] 中。

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

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

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

有些时候,内部的方法功能将会委托给一个名字相似的抽象操作符,比如普通对象中的 [[GetOwnProperty]]

[[GetOwnProperty]](p) 当带有参数 P 调用 O 内部的方法 [[GetOwnProperty]] 时,会进行以下步骤:

1、返回 !OrdinaryGetOwnProperty(O, P)

(将会在下一章节解释 ! 的意思)

OrdinaryGetOwnProperty 并不是一个内部的方法,因为它与任何对象都毫无干系,取而代之,这个对象做为参数被传入这个操作符。

OrdinaryGetOwnProperty 被称之为***普通的(ordinary)***,因为它操作普通的对象。ECMAScript 对象可以是普通对象,也可以是变异的对象(exotic),普通对象必须拥有一些默认行为,比如一组内部方法。如果一个对象偏离这些默认行为,它就是变异的。

最广为人知变异对象是 Array,因为它的长度属性以非默认方式运行 —— 设置长度属性,能够从数组中移除一个元素。

基础的内部方法列表可以从看到。

Completion records

那么 ?! 是什么意思了?为了理解它们,我们必须来看看 Completion records

Completion Record 是一个规范中的类型(仅仅定义在规范中),JavaScript 引擎并不需要一个相应的内置数据类型。

Completion Record :

名字

描述

[[Type]]

值可以是 nomal、break、continue、return 和 throw,除了 nomal,其他都是***中断完成***

[[Value]]

完成时,产生的值。比如函数的返回值,或者是一个异常(如果抛出)

[[Target]]

用来定向转移的标签

每个抽象操作符隐式返回一个 Completion Record,尽管它可能仅仅是返回了一个 Boolean 类型,它也会被 Completion Record 包裹着(详情可以查看 隐式的 Completion Values)。

注意点1:规范在这方面并不完全一致。因为存在一些直接返回裸露值的辅助函数,这些函数返回值按照原样使用,而不是从 Completion Record 中提取出值。通常可以从上下文中清楚看出。

注意点2:规范的编写者正在找寻一种能让 Completion Record 更加明确的方式。

如果一个程序抛出一场,这意味着需要返回一个有特定 [[type]] 的 Completion Record,它的 [[Value]] 是一个含有错误信息的对象。到现在为止,我们将会忽略 breakcontinuereturn[[type]]

ReturnIfAbrupt(argument) 将会进行以下步骤:

1、如果参数出错,返回这个参数 2、把参数赋值给 argument.[[value]]

这就是我们期望的一个 Completion Record。如果一个中断完成了,便立即放回。否则我们将会从 Completion Record 中取值。

ReturnIfAbrupt 看起来像是一个函数的调用,但实际上并不是。它导致返回ReturnIfAbrupt() 的函数返回,而不是 ReturnIfAbrupt 函数本身返回。它的行为更像是 C 语言中的宏(macro)。

ReturnIfAbrupt 能像下面这样使用:

1、令 objFoo()(obj 为 Completion Record) 2、ReturnIfAbrupt(obj) 3、Bar(obj)(如果进行到这步。obj 从 Completion Record 中提取的值)

因此 ? 的作用既是:?Foo() 等价于 ReturnIfAbrupt(Foo())。使用简写的形式是有用的,我们不必每次都写捕获错误的代码。

与此相似,!Foo() 等价于:

1、令 valFoo 2、断言,val 不是一个突然的中断 3、把 val 赋值给 val.[[value]]

使用这些知识,我们可以写出 Object.prototype.hasOwnProperty

Object.prototype.hasOwnProperty(P) 1、令 PToPropertyKey(V) 2、如果 P 是一个中断完成,返回 P 3、令 p.[[value]] 等于 p 4、令 OToObject(O) 5、如果 O 是一个中断完成,返回 O 6、令 O.[[value]] 等于 O 7、令 tempHasOwnProperty(O, P) 8、如果 temp 是中断完成,返回 temp 9、令 temptemp.[[value]] 10、返回 NormalCompletion

与此相似,我们也可以重写出 HasOwnProperty(O, P)

HasOwnProperty(O, P) 1、断言:Type(O) 是一个对象 2、断言:IsPropertyKey(P) 是 true 3、令 descO.[[GetOwnProperty]](P) 4、如果 desc 是一个中断完成,返回 O 5、令 desc.[[value]] 等于 desc 6、如果 descundefined,返回 NormalCompletion(false) 7、返回 NormalCompletion(true)

我们也可以重写不带 ! 的内部方法 [[GetOwnProperty]]

O.[[GetOwnProperty]] 1、令 tempOrdinaryGetOwnProperty(O, P) 2、断言:temp 不会是一个中断完成 3、令 temp.[[value]] 等于 temp 4、返回 NormalCompletion(temp)

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

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

总结

到现在,我们已经了解了阅读如 Object.prototype.hasOwnPropert 以及抽象操作符 HasOwnProperty 规范所需的知识点。它们仍然需要委托其他抽象操作符去工作,基于此博客,我们应该能了解到它们是如何工作的。接下来,我们将会遇见属性描述符,它们仅仅另外一种规范类型。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年03月02日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 语言类型和规范类型
  • 抽象操作符
  • 内部的插槽和函数
  • Completion records
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档