ECMAScript 双月报告:Intl.Enumeration 提案成功进入到 Stage 4

简介: ECMAScript 双月报告:Intl.Enumeration 提案成功进入到 Stage 4

在本次会议中,Intl.Enumeration 提案成功进入到 Stage 4,距离它在 2020 年 6 月的会议上进入到 Stage 1 已经过去了两年半的时间,其它备受关注的提案如 Explicit Resource Management[1]Set Methods[2] 也成功取得进展,进入到 Stage 3 阶段。

Stage 3 → Stage 4

从 Stage 3 进入到 Stage 4 有以下几个门槛:

  1. 必须编写与所有提案内容对应的 tc39/test262[3] 测试,用于给各大 JavaScript 引擎和 transpiler 等实现检查与标准的兼容程度,并且 test262 已经合入了提案所需要的测试用例;
  2. 至少要有两个实现能够兼容上述 Test 262 测试,并发布到正式版本中;
  3. 发起了将提案内容合入正式标准文本 tc39/ecma262[4] 的 Pull Request,并被 ECMAScript 编辑签署同意意见。

Intl.Enumeration

提案链接:proposal-intl-enumeration[5]

这一提案用于获取当前运行环境下国际化选项的支持值,如 calendar、currency、timeZone 等,其引入了 Intl.supportedValuesOf(key)方法来返回对应的所有支持值,如获取当前运行环境中所有支持的历法:

console.log(Intl.supportedValuesOf('calendar')); 
// ['buddhist', 'chinese', 'coptic', 'dangi', ...]

Stage 2 → Stage 3

提案从 Stage 2 进入到 Stage 3 有以下几个门槛:

  1. 撰写了包含提案所有内容的标准文本,并有指定的 TC39 成员审阅并签署了同意意见;
  2. ECMAScript 编辑签署了同意意见。

Set Methods

提案链接:proposal-set-methods[6]

此提案为 JavaScript 中的 Set 结构新增了一批内置方法,主要为集合相关,包括交集、并集、差集、子集等:

  • Set.prototype.intersection(other)
  • Set.prototype.union(other)
  • Set.prototype.difference(other)
  • Set.prototype.symmetricDifference(other)
  • Set.prototype.isSubsetOf(other)
  • Set.prototype.isSupersetOf(other)
  • Set.prototype.isDisjointFrom(other)

这些方法的入参均为另一个 Set 类型的数据,或者至少是实现了 .size .keys .has 三个方法的对象。

Well-formed Unicode strings

提案链接:proposal-is-usv-string[7]

ECMAScript 字符串都是 UTF-16 编码的字符串。在 Web API 中,我们可以发现有些 API (如 URL、URLSearchParams 等等系列 API)都声明了需要 USVString 作为参数。什么是 USVString?USV 代表 Unicode Scalar Value,即 Unicode 标量值。根据 Unicode 定义,Unicode 的码位(Code Point)可以分成几个类别,分别是图形码(Graphic),格式码(Format),控制码(Control),私有码(Private-Use),代理码(Surrogate),非字符码(Noncharacter),与保留码(Reserved)。而其中的代理码又分成了高位代理码与低位代码码,只有当一个高位代码码与一个低位代理码组合成一个代理码对,才是一个合法的 Unicode 字符。

目前,JavaScript 字符串并不限制这个字符串的值是否是合法的 Unicode 值,比如我们可以编码一个字符串只有高位代理码,而没有低位代理码等等。而如严格的 Web URL API 定义必须要求参数字符串是合法的 Unicode 标量值,因此我们需要有方法能够去区分一个字符串是否是合法的 Unicode 标量值。

这个提案提出为 ECMAScript 引入新的内置方法 String.prototype.isWellFormed,  用于检查这个字符串是否是一个合法的 Unicode 标量值:

'\ud800'.isWellFormed(); // => false
'\ud800\udc00'.isWellFormed(); // => true

另外此提案也提供了 String.prototype.toWellFormed 方法,来将普通字符串转换到一个格式正确的 USV 字符串。类似的,NodeJs 中也提供了 util.toUSVString 这样的方法来实现此功能。

你也可以使用其提供的 polyfill string.prototype.iswellformed[8]string.prototype.towellformed[9] 来提前试用。

Explicit Resource Management

提案链接:proposal-explicit-resource-management[10]

此提案旨在为 JavaScript 中引入显式的资源管理能力,通过统一的 using 关键字来标记当前块级作用域内的关键资源。

在此前,JavaScript 中对各种资源的管理方式并不统一,如在 Generator 函数中,通过 return 方法来提供执行清理逻辑的方式:

function * g() {
  const handle = acquireFileHandle();
  try {
    ...
  }
  finally {
    handle.release(); // 释放资源
  }
}
const obj = g();
try {
  const r = obj.next();
  ...
}
finally {
  obj.return(); // 显式调用函数 g 中的 finally 代码块
}

而通过 using 关键字,其可以被简化为如下的方式:

function * g() {
  using handle = acquireFileHandle(); // 定义与代码块关联的资源
}
{
  using obj = g(); // 显式声明资源
  const r = obj.next();
} // 自动调用释放逻辑

除此以外,NodeJs FileHandles 上的 handle.close() 方法,WHATWG Stream Readers 上的 reader.releaseLock() 均可以使用这种方式来简化资源的管理。

实际上,这里的“显式”对应的是此前如 WeakSet 与 WeakMap 这样,会由运行时作为垃圾回收的一部分进行的“隐式”工作。显式资源管理意味着用户主动声明块级作用域内依赖的资源,通过 Symbol.disposable[11] 这样的命令式或 using 这样的声明式,然后在离开作用域时自动地释放这些标记的资源。

Stage 1 → Stage 2

从 Stage 1 进入到 Stage 2 需要完成撰写包含提案所有内容的标准文本的初稿。

ArrayBuffer transfer

提案链接:proposal-arraybuffer-transfer[12]

这一提案属于 proposal-resizablearraybuffer[13] 提案的衍生,其引入了 ArrayBuffer.prototype.transfer 方法,来支持对 ArrayBuffer 的所有权转移能力。

在 JavaScript 中,可转移对象指的是拥有可在不同上下文间转移的资源的对象,在转移资源后,原始上下文中的对象将不再指向资源,只有新的上下文持有资源的所有权。这一能力通常用于确保在同一时刻只有一个线程能够访问资源。更常见的一个例子是在 Web Worker 场景下,将可转让对象(比如一个 ArrayBuffer)在主线程与工作线程之间传递,传递方仍然持有原始 ArrayBuffer 对象,但其 byteLength 为0,同时无法再对其进行写入。这一过程无需经过任何拷贝操作,也就意味着在数据量较大时能够有明显的性能提升。

此前我们并不能直接将一个 ArrayBuffer 的资源所有权转移到另一个 ArrayBuffer 对象,以此来避免原始的缓冲区输入被篡改,而只能使用 slice 方法来复制一个 ArrayBuffer 对象,如以下这个例子:

function validateAndWriteSafeButSlow(arrayBuffer) {
 // 复制一份,避免缓冲区被篡改
  const copy = arrayBuffer.slice();
  await validate(copy);
  await fs.writeFile("data.bin", copy);
}
const data = new Uint8Array([0x01, 0x02, 0x03]);
validateAndWrite(data.buffer);
setTimeout(() => {
  // 篡改数据
  data[0] = data[1] = data[2] = 0x00;
}, 50);

这种方式需要将原本的 ArrayBuffer 中的每个字节进行复制,然后开辟新的缓冲区存放,在数据量较大将导致性能问题。而现在,我们可以使用 transfer 方法来直接转移其所有权,使得其无法被篡改:

function validateAndWriteSafeAndFast(arrayBuffer) {
  // 转移所有权,并直接移动而非复制数据
  const owned = arrayBuffer.transfer();
  assert(arrayBuffer.detached);
  await validate(owned);
  await fs.writeFile("data.bin", owned);
}

这里的 arrayBuffer.detached 属性也来自与此提案,用于作为一种清晰且权威的方式,来检查一个 ArrayBuffer 对象是否已从缓冲区分离。

Stage 0 → Stage 1

从 Stage 0 进入到 Stage 1 有以下门槛:

  1. 找到一个 TC39 成员作为 champion 负责这个提案的演进;
  2. 明确提案需要解决的问题与需求和大致的解决方案;
  3. 有问题、解决方案的例子;
  4. 对 API 形式、关键算法、语义、实现风险等有讨论、分析。

Stage 1 的提案会有可预见的比较大的改动,以下列出的例子并不代表提案最终会是例子中的语法、语义。

Intl MessageResource

提案链接:proposal-intl-message-resource[14]

Intl MessageResource 提案是对 Intl.MessageFormat 提案的进一步补充,用于实现一次性对一组资源消息的翻译能力,这是因为 UI 界面中通常同时会存在一组相关联的消息需要翻译,如对话框等。

对于 Intl.MessageFormat 提案,我们的使用方式是这样的,首先给到 MF2 定义:

morning_greeting = {早上好,{$user}!}
new_notifications [$count] =
  [0]   你现在还没有信息
  [one] 你收到新信息了~
  [_]   你收到了 {$count} 条新信息,快打开看看吧!

MF2 的全称为 MessageFormat 2.0,是由 message-format-wg 制定的,统一的消息描述规范。它是编程语言无关的,目前实现了 MessageFormat 2.0 的语言主要包括 JavaScript 和 Java。

这里定义了无消息、单条消息、多条消息的几种情况,然后在 JavaScript 中就可以通过创建 Intl .MessageFormat 的实例,来进行消息的解析:

const resource = ... // 即以上的 MF2 定义
const mf1 = new Intl.MessageFormat(resource, ['en']);
const msg1 = mf.resolveMessage('new_notifications', { count: 3 });
msg1.toString(); // '你收到了 3 条新信息,快打开看看吧!'
const mf2 = new Intl.MessageFormat(resource, ['en']);
const msg2 = mf.resolveMessage('morning_greeting', { user: '小明' });
msg2.toString(); // '早上好,小明'

可以看到,对于同一个 MF2 定义,需要创建两个 Intl.MessageFormat 实例来分别进行解析。而 Intl Message Resource 提案为 Intl.MessageFormat 新增了 parseResource 静态方法,使得我们可以一次性完成对所有消息资源的解析:

const resource = ... // 即以上的 MF2 定义
const res = Intl.MessageFormat.parseResource(resource, ['en']);
const greeting = res.get('morning_greeting').resolveMessage({ user: '小明' });
greeting.toString(); // '早上好,小明'
const notifications = res.get('new_notifications').resolveMessage({ count: 3 });
notifications.toString(); // '你收到了 3 条新信息,快打开看看吧!'

另外,MessageFormatter 系列相关提案将引入的 API 具体格式还未完全确定,最终将取决于 MF2 工作组最终为 MF2 落地的语法。

Intl.era and monthCode

提案链接:proposal-intl-era-monthcode[15]

这一提案属于 ECMAScript 402 中的 Intl 提案,与我们更熟悉的 Temporal 提案不同的是,Temporal 仅对 ISO8601 时间格式与 UTC 时区下的行为做了明确定义,对 ISO8601 以外的时间格式和 UTC 以外的时区,只提供了最基本的定义。而 Intl.era 提案旨在对这些规范细节进行进一步的完善。

这一提案之所以没有被作为 Temporal 提案的一部分,原因在于 Temporal 是 ECMA262 规范(即 ECMAScript)的一部分,其需要在所有支持 ECMAScript 的环境中运行并保持一致性,而 Intl 提案所属的 ECMAScript 402 作为 ECMAScript 的国际化标准,其在运行时可能会受到限制,如仅保留少数语言支持,此提案的行为也可能受到影响。

Mass Proxy Revocation

提案链接:proposal-mass-proxy-revocation[16]

在 JavaScript 中,我们可以通过 Proxy.revocable 方法来创建一个可被撤销的代理对象,其返回值中将包含一个 revoke方法,调用此方法就将撤销掉一起生成的代理对象,而后续对此代理对象的所有可代理操作都将抛出错误。但这种方式仅适用于使用 revocable 方法创建的代理对象,同时需要为每个对象都调用一次 revoke方法。

而此提案引入了 createSignal 与 finalizeSignal 方法,来支持一次性对一批 Proxy 对象的撤销,甚至是直接通过 new Proxy 创建的代理对象也能够通过这种方式撤销,其使用方式大致如下:

const s1 = Proxy.createSignal();
const p1 = new Proxy([], {}, { signal: s1 });
const { proxy: p2, revoke } = Proxy.revocable({}, {}, { signal: s1 });
Proxy.finalizeSignal(s1); // p1 与 p2 都将被撤销
相关文章
|
存储 Web App开发 JavaScript
ECMAScript Async Context 提案介绍
ECMAScript Async Context 提案介绍
23378 1
ECMAScript Async Context 提案介绍
|
存储 机器学习/深度学习 监控
ECMAScript 双月报告:TC39 2023年3月会议提案进度汇总
在本次会议中,共有 9 个提案实现了 Stage 推进,其中阿里巴巴主导的 AsyncContext 提案进入到了 Stage 2。另外,有 4 个提案成功进入到 Stage 1,包括 Promise.withResolvers 以及 Class Method Param Decorators 等此前就广受关注的内置方法和语法提案。Stage 2 → Stage 3当一个提案进入 Stage 3 
|
存储 监控 JavaScript
ECMAScript 双月报告:Async Context 提案成功进入到 Stage 1
ECMAScript 双月报告:Async Context 提案成功进入到 Stage 1
179 0
|
JavaScript 算法 前端开发
ECMAScript 双月报告:TC39 2022年12月会议提案进度汇总
在本次会议中,Intl.Enumeration 提案成功进入到 Stage 4,距离它在 2020 年 6 月的会议上进入到 Stage 1 已经过去了两年半的时间,其它备受关注的提案如 Explicit Resource Management 与 Set Methods也成功取得进展,进入到 Stage 3 阶段。Stage 3 → Stage 4从 Stage 3 进入到 Stage 4 有以
|
存储 Web App开发 JSON
ECMAScript 双月报告:findLast 提案成功进入到 Stage 4
本次会议中,findLast 提案成功进入到了 Stage 4,这是第二个由中国开发者推动进入到 Stage 4 的提案。另外,较受关注的 String Dedent 与 JSON.parse source text access 等提案也在本次会议中取得了阶段性进展。
321 0
ECMAScript 双月报告:findLast 提案成功进入到 Stage 4
|
前端开发 JavaScript 算法
ECMAScript 双月报告:Array.fromAsync 进入 Stage 3
在本次 TC39 会议中,或许是由于在亚洲时区(东京时间)举办的原因,整体提交的提案数量较少,也仅有三个提案取得了阶段性进展。另外,本次会议中没有提案进入到 Stage 4 阶段。
273 0
|
JSON JavaScript 前端开发
关于ECMA、ECMAScript、TC39、ES、Stage等概念的科普
关于ECMA、ECMAScript、TC39、ES、Stage等概念的科普
362 0
关于ECMA、ECMAScript、TC39、ES、Stage等概念的科普
|
存储 JavaScript 前端开发
ECMAScript 双月报告:装饰器提案进入 Stage 3
ECMAScript 双月报告:装饰器提案进入 Stage 3
1074 0
|
JavaScript 前端开发 Cloud Native
我国首个 JS 语言提案在 ECMA 进入 Stage 3
近期,在 ECMA 标准化组织的 TC39 技术委员会上,阿里巴巴前端标准化小组与淘系技术提出的 JavaScript 标准提案《Error Cause》进入了 Stage 3,将开始在 JavaScript 引擎中开始实现,并在浏览器、Node.js 实验性实施,是中国首个推进到 EcmaScript 的语言,将成为官方标准的自主技术提案。
我国首个 JS 语言提案在 ECMA 进入 Stage 3
|
JavaScript 前端开发 API
ECMAScript 双月报告:Realms 提案进入 Stage 3(2021/07)
7月的 TC39 会议在上周结束了。这次的会议有如 private-in 等提案进入了 Stage 4,Realms、`Object.hasOwn` 等提案进入了 Stage 3,相信很快大家就可以在开发者版本的浏览器、最新版 Node.js 中见到这些 API 了。那么这些提案提供了什么样的能力,我们该如何使用?
ECMAScript 双月报告:Realms 提案进入 Stage 3(2021/07)