Web IDL

现行标准 — 最后更新

参与:
GitHub whatwg/webidl (新问题, 开放问题)
在Matrix上聊天
提交记录:
GitHub whatwg/webidl/commits
此提交的快照
@webidl
测试:
web-platform-tests webidl/ (正在进行的工作)
翻译 (非规范):
日本语
简体中文

摘要

该标准定义了一种接口定义语言,Web IDL,可用于描述旨在由Web浏览器实现的接口。

1. 简介

本节为资料性内容。

本标准定义了一种接口定义语言 Web IDL,可用于描述拟在 Web 浏览器中实现的接口。Web IDL 是一种 IDL 变体,具有许多特性,能够更容易地指定 Web 平台中常见脚本对象的行为。还详细说明了使用 Web IDL 描述的接口如何与 JavaScript 执行环境中的构造相对应。

具体而言,Web IDL 提供了一种语法,用于指定 Web 平台对象的表面 API 以及 JavaScript 绑定,详细说明了这些 API 如何以 JavaScript 构造的形式体现。这确保了常见任务(例如安装全局属性、处理数值输入或暴露迭代行为)在各个 Web 平台规范中保持统一:这些规范使用 Web IDL 描述其接口,然后使用文章来指定 API 特定的细节。

术语“JavaScript”用于指代 ECMA-262,而不是官方术语 ECMAScript,因为 JavaScript 这一术语更为人所熟知。

2. 接口定义语言

本节描述了一种语言,Web IDL,可以用于为 Web 平台中的 API 定义接口。定义 Web API 的规范可以包含一个或多个 IDL 片段, 描述该规范所定义的 API 的接口(对象可以展示的状态和行为)。 一个 IDL 片段 是一系列与 Definitions 语法符号相匹配的定义。 一个实现支持的 IDL 片段 集合没有顺序。 请参见 IDL 语法 获取完整的语法以及符号使用说明。

可以出现在 IDL 片段 中的各种 定义 包括:接口部分接口定义接口混入部分混入定义回调函数回调接口命名空间部分命名空间定义字典部分字典定义类型定义 以及 包含声明。 这些定义将在以下部分中进行详细定义。

每个 定义(与 Definition 相匹配) 可以由一系列 扩展属性(与 ExtendedAttributeList 相匹配)开头, 用于控制定义将在语言绑定中如何处理。 本规范定义的与语言绑定无关的扩展属性将在 § 2.14 扩展属性 中讨论, 而与 JavaScript 语言绑定相关的扩展属性则在 § 3.3 扩展属性 中讨论。

[扩展属性]
  接口 标识符 {
    /* 接口成员... */
  };
  
Definitions ::
      ExtendedAttributeList Definition Definitions
      ε
  
Definition ::
      CallbackOrInterfaceOrMixin
      Namespace
      Partial
      Dictionary
      Enum
      Typedef
      IncludesStatement
  

以下是一个 IDL 片段 的示例。

[暴露=Window]
  接口 Paint { };
  
  [暴露=Window]
  接口 SolidColor : Paint {
    属性 双精度浮点数 red;
    属性 双精度浮点数 green;
    属性 双精度浮点数 blue;
  };
  
  [暴露=Window]
  接口 Pattern : Paint {
    属性 DOMString imageURL;
  };
  
  [暴露=Window]
  接口 GraphicalWindow {
    构造函数();
    只读 属性 无符号 长整数 width;
    只读 属性 无符号 长整数 height;
  
    属性 Paint currentPaint;
  
    未定义 drawRectangle(双精度浮点数 x, 双精度浮点数 y, 双精度浮点数 width, 双精度浮点数 height);
  
    未定义 drawText(双精度浮点数 x, 双精度浮点数 y, DOMString text);
  };
  

在此,定义了四个 接口GraphicalWindow 接口有两个 只读 属性, 一个可写属性,以及两个 操作。实现 GraphicalWindow 接口的对象将以适合使用的语言的方式暴露这些属性和操作。

在 JavaScript 中,IDL 接口上的属性将作为访问器属性暴露, 操作将作为数据属性,其值为 内置函数对象, 位于所有 GraphicalWindow 对象的原型对象中;每个实现 GraphicalWindow 的 JavaScript 对象都会在其原型链中包含该原型对象。

出现在 GraphicalWindow 上的 构造函数操作 导致在 JavaScript 实现中存在一个 构造函数, 因此调用 new GraphicalWindow() 将返回一个实现了该接口的新对象。

所有 接口 都有 [暴露] 扩展属性,这确保了接口 只在 领域 中可用,其 全局对象Window 对象。

2.1. 名称

每个 接口部分接口定义命名空间部分命名空间定义字典部分字典定义枚举回调函数回调接口类型定义 (统称为 命名定义) 以及每个 常量属性, 和 字典成员 都有一个 标识符,某些 操作 也有。 该标识符通过声明中的 identifier 令牌确定:

注意: 当用于声明 特殊类型的操作(如 getter 或 setter)时,操作可以没有标识符。

对于所有这些构造,标识符 是删除任何前导 U+005F (_) 后的 identifier 令牌的值。

注意: 前导 U+005F (_) 用于防止标识符看起来像保留字,例如,可以定义一个名为“interface”的接口。前导 U+005F (_) 会被删除以取消转义标识符。

操作参数可以采用稍微宽泛的标识符集。在操作声明中,参数的标识符在其类型之后立即指定,由 identifier 令牌或与 ArgumentNameKeyword 符号匹配的关键字之一表示。如果使用这些关键字之一,则无需使用前导下划线进行转义。

interface interface_identifier {
    return_type operation_identifier(argument_type argument_identifier /* , ... */);
  };
  
ArgumentNameKeyword ::
      async
      attribute
      callback
      const
      constructor
      deleter
      dictionary
      enum
      getter
      includes
      inherit
      interface
      iterable
      maplike
      mixin
      namespace
      partial
      readonly
      required
      setlike
      setter
      static
      stringifier
      typedef
      unrestricted
  

如果使用了 identifier 令牌,则操作参数的 标识符 是删除任何前导 U+005F (_) 后的该令牌的值。 如果使用的是 ArgumentNameKeyword 关键字令牌,则操作参数的 标识符 就是该令牌本身。

上述 IDL 构造的任何一个的 标识符(操作参数除外)不得为 "constructor"、 "toString" 或以 U+005F (_) 开头。这些被称为 保留标识符

尽管 "toJSON" 标识符 不是 保留标识符, 但它必须仅用于将对象转换为 JSON 类型常规操作,如 § 2.5.3.1 toJSON 所述。

注意: 某些构造的标识符名称可能在后续部分中有进一步限制。

在给定实现支持的 IDL 片段 集合中,每个 接口命名空间字典枚举回调函数回调接口类型定义标识符 不得与任何其他 接口命名空间字典枚举回调函数回调接口类型定义 的标识符相同。

IDL 片段 中,对 定义 的引用无需出现在所引用定义的声明之后。引用也可以跨 IDL 片段 进行。

因此,以下 IDL 片段 是有效的:

[Exposed=Window]
  interface B : A {
    undefined f(SequenceOfLongs x);
  };
  
  [Exposed=Window]
  interface A {
  };
  
  typedef sequence<long> SequenceOfLongs;
  

以下 IDL 片段 展示了如何为定义和 接口成员 赋予标识符。

// Typedef identifier: "number"
  typedef double number;
  
  // Interface identifier: "System"
  [Exposed=Window]
  interface System {
  
    // Operation identifier:          "createObject"
    // Operation argument identifier: "interface"
    object createObject(DOMString _interface);
  
    // Operation argument identifier: "interface"
    sequence<object> getObjects(DOMString interface);
  
    // Operation has no identifier; it declares a getter.
    getter DOMString (DOMString keyName);
  };
  
  // Interface identifier: "TextField"
  [Exposed=Window]
  interface TextField {
  
    // Attribute identifier: "const"
    attribute boolean _const;
  
    // Attribute identifier: "value"
    attribute DOMString? _value;
  };
  

请注意,虽然 TextField 接口上的第二个 属性不需要使用下划线转义(因为 "value" 不是 IDL 语法中的关键字),但它仍然会被解除转义以获得该属性的 标识符

2.2. 接口

IDL 片段用于描述面向对象的系统。在这些系统中,对象是具有标识性并封装状态和行为的实体。接口是一个定义(匹配interface InterfaceRest),声明了一个实现该接口的对象将公开的某些状态和行为。

[extended_attributes]
  interface identifier {
    /* interface_members... */
  };
  

接口是 接口成员(匹配InterfaceMembers)的规范。这些成员是在接口声明的大括号之间出现的成员。

Web IDL 中的接口描述了实现该接口的对象的行为。在面向对象语言的绑定中,预计实现特定 IDL 接口的对象提供检查和修改对象状态以及调用接口描述的行为的方式。

接口可以定义为从另一个接口继承。如果接口的标识符后跟 U+003A (:) 和一个标识符,那么该标识符标识继承的接口。实现继承接口的对象也将实现该继承接口。因此,对象还将拥有与继承接口的接口成员相对应的成员。

interface identifier : identifier_of_inherited_interface {
    /* interface_members... */
  };
  

成员出现的顺序对JavaScript 绑定中的属性枚举具有重要意义。

接口可以指定一个与继承接口中的同名接口成员。实现派生接口的对象将在派生接口上公开该成员。是否可以在对象上访问被覆盖的成员取决于语言绑定的具体情况。

请考虑以下两个接口。

[Exposed=Window]
  interface A {
    undefined f();
    undefined g();
  };
  
  [Exposed=Window]
  interface B : A {
    undefined f();
    undefined g(DOMString x);
  };
  

在 JavaScript 语言绑定中,B的实例将拥有如下的原型链:

[Object.prototype: the Object prototype object]
       ↑
  [A.prototype: interface prototype object for A]
       ↑
  [B.prototype: interface prototype object for B]
       ↑
  [instanceOfB]
  

在 JavaScript 中调用instanceOfB.f()将调用定义在B上的f。然而,可以通过调用A.prototype.f.call(instanceOfB)在实现B的对象上仍然调用A中的f。

给定接口A继承接口A直接或间接继承的所有接口的集合。如果A不从另一个接口继承,则集合为空。否则,集合包括A继承的接口B以及B的所有继承接口

接口声明不得使其继承层次结构形成循环。即,接口A不能继承自它自身,也不能继承自继承自A的另一个接口B,等等。

接口I包含的继承接口列表定义如下:
  1. result为« »。

  2. interfaceI

  3. interface不为null时:

    1. interface附加到result

    2. 如果有的话,将interface设置为I继承的接口,否则设置为null。

  4. 返回result

请注意,一般的接口多重继承不被支持,且对象也不能实现任意集合的接口。可以定义对象来实现给定的单个接口A,这意味着它也实现了A的所有继承接口。此外,可以使用includes 语句来定义实现接口A的对象将始终包含A 接口混入的成员

每个接口成员前可以有一个扩展属性列表(匹配ExtendedAttributeList),该属性可以控制接口成员在语言绑定中的处理方式。

[extended_attributes]
  interface identifier {
  
    [extended_attributes]
    const type constant_identifier = 42;
  
    [extended_attributes]
    attribute type identifier;
  
    [extended_attributes]
    return_type identifier(/* arguments... */);
  };
  

接口的 IDL 可以通过使用部分接口定义(匹配partial interface PartialInterfaceRest)拆分为多个部分。部分接口定义的标识符必须与接口定义的标识符相同。每个部分接口中出现的成员都被视为接口本身的成员。

interface SomeInterface {
    /* interface_members... */
  };
  
  partial interface SomeInterface {
    /* interface_members... */
  };
  

注意:部分接口定义旨在作为规范编辑辅助工具,允许将接口定义分散在文档的多个部分,有时甚至是多个文档中。

接口定义及其任何部分接口定义的出现顺序没有关系。

注意:部分接口定义不能指定接口继承自另一个接口。继承必须在原始接口定义中指定。

相关语言绑定决定接口如何对应于该语言中的结构。

以下扩展属性适用于接口:[CrossOriginIsolated],[Exposed],[Global],[LegacyFactoryFunction],[LegacyNoInterfaceObject],[LegacyOverrideBuiltIns],[LegacyWindowAlias],以及[SecureContext]。

以下扩展属性适用于部分接口:[CrossOriginIsolated],[Exposed],[LegacyOverrideBuiltIns],以及[SecureContext]。

接口必须使用[Exposed] 扩展属性进行注解。

接口interface限定名称定义如下:

  1. identifierinterface标识符

  2. 如果interface具有[LegacyNamespace]扩展属性,则:

    1. namespace为[LegacyNamespace]扩展属性的标识符参数。

    2. 返回由namespaceidentifier连接,用分隔符 U+002E (.)。

  3. 返回identifier

CallbackOrInterfaceOrMixin ::
      callback CallbackRestOrInterface
      interface InterfaceOrMixin
  
InterfaceOrMixin ::
      InterfaceRest
      MixinRest
  
InterfaceRest ::
      identifier Inheritance { InterfaceMembers } ;
  
Partial ::
      partial PartialDefinition
  
PartialDefinition ::
      interface PartialInterfaceOrPartialMixin
      PartialDictionary
      Namespace
  
PartialInterfaceOrPartialMixin ::
      PartialInterfaceRest
      MixinRest
  
PartialInterfaceRest ::
      identifier { PartialInterfaceMembers } ;
  
InterfaceMembers ::
      ExtendedAttributeList InterfaceMember InterfaceMembers
      ε
  
InterfaceMember ::
      PartialInterfaceMember
      Constructor
  
PartialInterfaceMembers ::
      ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers
      ε
  
PartialInterfaceMember ::
      Const
      Operation
      Stringifier
      StaticMember
      Iterable
      AsyncIterable
      ReadOnlyMember
      ReadWriteAttribute
      ReadWriteMaplike
      ReadWriteSetlike
      InheritAttribute
  
Inheritance ::
      : identifier
      ε
  

以下IDL 片段展示了两个相互引用的接口的定义。HumanDog都继承自Animal。实现这两个接口中的任意一个的对象都将具有name属性。

[Exposed=Window]
  interface Animal {
    attribute DOMString name;
  };
  
  [Exposed=Window]
  interface Human : Animal {
    attribute Dog? pet;
  };
  
  [Exposed=Window]
  interface Dog : Animal {
    attribute Human? owner;
  };
  

以下IDL 片段定义了简化版本的 DOM 接口和一个回调接口

[Exposed=Window]
  interface Node {
    readonly attribute DOMString nodeName;
    readonly attribute Node? parentNode;
    Node appendChild(Node newChild);
    undefined addEventListener(DOMString type, EventListener listener);
  };
  
  callback interface EventListener {
    undefined handleEvent(Event event);
  };
  

普通对象可以实现一个回调接口,如EventListener

var node = getNode();                                // 获取 Node 实例。

  var listener = {
    handleEvent: function(event) {
      // ...
    }
  };
  node.addEventListener("click", listener);            // 这可以正常工作。

  node.addEventListener("click", function() { ... });  // 这也是可以的。
  

然而,这类对象无法实现一个接口,比如Node

var node = getNode();  // 获取 Node 实例。

  var newNode = {
    nodeName: "span",
    parentNode: null,
    appendChild: function(newchild) {
      // ...
    },
    addEventListener: function(type, listener) {
      // ...
    }
  };
  node.appendChild(newNode);  // 这将抛出一个 TypeError 异常。
  

2.3. 接口混入

接口混入 是一个定义(匹配 interfaceMixinRest),它声明了可以由一个或多个接口 包含的状态和行为,这些行为会通过实现接口包含接口混入的对象来公开。

interface mixin identifier {
    /* mixin_members... */
  };
  

注意:接口混入部分接口类似,旨在作为规范编辑的工具,允许将一组功能归为一组,并在多个接口中包含,可能跨多个文档。它们不打算通过语言绑定暴露。有关何时选择部分接口接口混入部分接口混入的指导可以在§ 2.3.1 使用混入和部分接口中找到。

接口混入常量常规操作常规属性字符串化器的集合描述,它们是定义在接口混入声明中的。

这些常量常规操作常规属性字符串化器描述了可以由对象实现的行为,就好像它们是由接口指定的一样,这些接口包含了这些混入。

与接口一样,接口混入的IDL可以通过使用部分接口混入定义分成多个部分(匹配 partial interfaceMixinRest)。部分接口混入的标识符必须与其相应的接口混入相同。

interface mixin SomeMixin {
    /* mixin_members... */
  };
  
  partial interface mixin SomeMixin {
    /* mixin_members... */
  };
  

接口成员的出现顺序在JavaScript绑定中的属性枚举具有意义。

需要注意的是,与接口字典不同,接口混入并不创建类型。

在本规范中定义的扩展属性中,只有[CrossOriginIsolated]、[Exposed]和[SecureContext]适用于接口混入

包含语句 是一个定义(匹配 IncludesStatement),用于声明实现某个接口的所有对象必须额外包含指定的成员

interface_identifier includes mixin_identifier;
  

第一个标识符必须引用一个接口I。第二个标识符必须引用一个接口混入M

M的每个成员都被视为每个接口 IJK 等的成员,这些接口包含M,就好像每个成员都被复制了一样。因此,对于M的某个成员m,接口I被认为有一个成员mI,接口J被认为有一个成员mJ,接口K被认为有一个成员mK,以此类推。mImJmK宿主接口分别是IJK

注意:在 JavaScript 中,这意味着每个作为成员声明的接口混入M常规操作,并作为具有内置函数对象值的数据属性公开的,在每个包含混入M接口接口原型对象中都是一个独立的内置函数对象。同样,对于属性,每个访问器属性的副本都有独立的内置函数对象用于其获取器和设置器。

includes 声明的出现顺序会影响接口混入被其宿主接口 包含的顺序。

成员的顺序并没有明确规定,特别是当接口混入在不同文档中定义时。这在问题 #432中有所讨论。

本规范中定义的扩展属性不适用于includes 声明

以下IDL 片段定义了一个接口Entry,以及一个接口混入Observableincludes 声明指定了Observable成员会始终包含在实现Entry的对象上。

interface Entry {
    readonly attribute unsigned short entryType;
    // ...
  };
  
  interface mixin Observable {
    undefined addEventListener(DOMString type,
                          EventListener listener,
                          boolean useCapture);
    // ...
  };
  
  Entry includes Observable;
  

因此,在 JavaScript 实现中,每个Entry的原型链上都会有一个addEventListener属性:

var e = getEntry();          // 获取一个 Entry 的实例。
  typeof e.addEventListener;   // 结果为 "function"。
  
CallbackOrInterfaceOrMixin ::
      callback CallbackRestOrInterface
      interface InterfaceOrMixin
  
InterfaceOrMixin ::
      InterfaceRest
      MixinRest
  
Partial ::
      partial PartialDefinition
  
PartialDefinition ::
      interface PartialInterfaceOrPartialMixin
      PartialDictionary
      Namespace
  
MixinRest ::
      mixin identifier { MixinMembers } ;
  
MixinMembers ::
      ExtendedAttributeList MixinMember MixinMembers
      ε
  
MixinMember ::
      Const
      RegularOperation
      Stringifier
      OptionalReadOnly AttributeRest
  
IncludesStatement ::
      identifier includes identifier ;
  

2.3.1. 使用 mixins 和 partials

本节是说明性的。

接口混入允许在多个 接口之间共享属性常量操作。如果你只打算扩展一个接口,可以考虑使用部分接口

例如,不要这样做:

interface mixin WindowSessionStorage {
    readonly attribute Storage sessionStorage;
  };
  Window includes WindowSessionStorage;
  

可以这样做:

partial interface Window {
    readonly attribute Storage sessionStorage;
  };
  

此外,你还可以依赖其他规范中暴露的接口混入,以针对常见用例,如在窗口和工作线程上下文中暴露一组属性常量操作

例如,与其使用常见但冗长的方式:

interface mixin GlobalCrypto {
    readonly attribute Crypto crypto;
  };
  
  Window includes GlobalCrypto;
  WorkerGlobalScope includes GlobalCrypto;
  

你可以使用部分接口混入扩展WindowOrWorkerGlobalScope 接口混入

partial interface mixin WindowOrWorkerGlobalScope {
    readonly attribute Crypto crypto;
  };
  

2.4. 回调接口

回调接口是一个符合callback interface identifier { CallbackInterfaceMembers } ;定义。它可以由任何对象实现,如§ 2.12 实现接口的对象中描述的那样。

注意: 回调接口不是一个接口。其名称和语法来源于本标准的早期版本,当时这些概念有更多的共通点。

回调接口是对一组回调接口成员(符合CallbackInterfaceMembers)的规范。这些成员是在接口声明中的大括号之间出现的成员

callback interface identifier {
    /* interface_members... */
  };

注意: 另请参阅类似名称的回调函数定义。

回调接口必须定义一个常规操作

规范作者不应定义回调接口,除非需要描述现有 API 的要求。相反,应使用回调函数

EventListener定义为回调接口是一个例子,它需要允许具有特定属性(此处为handleEvent)的对象被认为实现了该接口。对于新 API 以及没有兼容性问题的 API,使用回调函数将仅允许函数对象(在 JavaScript 语言绑定中)。

回调接口如果声明了常量,必须使用 [Exposed] 扩展属性进行注释。

CallbackRestOrInterface ::
      CallbackRest
      interface identifier { CallbackInterfaceMembers } ;
  
CallbackInterfaceMembers ::
      ExtendedAttributeList CallbackInterfaceMember CallbackInterfaceMembers
      ε
  
CallbackInterfaceMember ::
      Const
      RegularOperation
  

2.5. 成员

接口, 接口混入, 和 命名空间 是一组 成员 的规范(分别对应 InterfaceMembers, MixinMembers, 和 NamespaceMembers),这些成员是 常量, 属性, 操作, 以及声明之间的大括号内出现的其他声明。 属性 描述了实现 接口接口混入命名空间 的对象将暴露的状态,操作 则描述了可以在该对象上调用的行为。常量 声明了命名的常量值,方便系统中的对象用户使用。

接口 包含 一个 接口混入 时,接口混入的每个 成员 也被视为该接口的成员。相比之下,继承 的接口成员不被视为该接口的成员。

对于在 接口接口混入 上定义的各种 成员,其 构造函数步骤getter 步骤setter 步骤方法步骤 可以访问 this 值,该值是 接口 类型的 IDL 值,其成员是在其声明中或 包含 接口混入 中声明的成员。

Setter 步骤 还可以访问 给定值, 该值是 属性 声明的类型的 IDL 值。

接口接口混入回调接口命名空间 各自支持不同的 成员 集合, 其分别在 § 2.2 接口§ 2.3 接口混入§ 2.4 回调接口§ 2.6 命名空间 中规定,并在以下信息表中总结:

接口 回调接口 接口混入 命名空间
常量
常规属性 只读 属性
静态属性
常规操作
字符串化器
特殊操作
静态操作
可迭代声明
异步可迭代声明
类似Map的声明
类似Set的声明

2.5.1. 常量

常量 是一个声明(对应 Const),用于将常量值绑定到名称。 常量可以出现在 接口回调接口 上。

过去常量主要用于以枚举方式定义命名的整数代码。Web 平台正在逐步远离这种设计模式,转而使用字符串。 编辑希望使用此功能时,强烈建议在继续之前通过 提交问题 进行讨论。

const type constant_identifier = 42;

标识符不能与另一个定义在相同 接口回调接口 上的 接口成员回调接口成员 的标识符相同。 此外,标识符不能为 "length", "name" 或 "prototype"。

注意: 这三个名称是 JavaScript 语言绑定中定义在 接口对象 上的属性名称。

常量的类型(对应 ConstType)不能是除 原始类型 以外的任何类型。 如果使用了 标识符,它必须引用一个其类型为原始类型的 typedef

常量声明的 ConstValue 部分给出了常量的值,可以是两个布尔字面量标记之一(truefalse),一个 整数 标记, 一个 小数 标记, 或三个特殊的浮点常量值之一(-Infinity, InfinityNaN)。

注意: 除了字符串和空序列之外,这些值还可用于指定 字典成员的默认值可选参数的默认值。 请注意,字符串、空序列 [] 和默认字典 {} 不能用作 常量 的值。

布尔字面量标记 truefalse 的值是 IDL 中的 booleantruefalse

整数标记 integer 的值是一个整数,其值如下确定:

  1. S 为整数标记匹配的一系列 标量值

  2. 如果 S 以 U+002D (-) 开头,则 sign 为 −1,否则为 1。

  3. 根据 标量值 的后续字符,确定数值的进制:

    U+0030 (0), U+0058 (X)
    U+0030 (0), U+0078 (x)

    进制为 16。

    U+0030 (0)

    进制为 8。

    其他情况

    进制为 10。

  4. 将所有余下的 标量值 解释为以 base 进制表示的整数。

  5. 返回 sign × number

常量值为 Infinity, -InfinityNaN 时,该值为 IEEE 754 单精度或双精度浮点数,具体取决于常量、字典成员或可选参数的类型:

类型 无限制浮点数,常量值 Infinity

值为 IEEE 754 单精度正无穷大值。

类型 无限制双精度浮点数,常量值 Infinity

值为 IEEE 754 双精度正无穷大值。

类型 无限制浮点数,常量值 -Infinity

值为 IEEE 754 单精度负无穷大值。

类型 无限制双精度浮点数,常量值 -Infinity

值为 IEEE 754 双精度负无穷大值。

类型 无限制浮点数,常量值 NaN

值为 IEEE 754 单精度 NaN 值,位模式为 0x7fc00000。

类型 无限制双精度浮点数,常量值 NaN

值为 IEEE 754 双精度 NaN 值,位模式为 0x7ff8000000000000。

小数 标记的类型与其作为常量、字典成员或可选参数值的类型相同。 小数 标记的值不得超出其类型的有效值范围,如 § 2.13 类型 中所述。 同时,Infinity-InfinityNaN 不得 用作 floatdouble 的值。

null 标记的值是 null 的特殊值,它是 可为空类型 的成员。 null 标记的类型与其作为常量、字典成员或可选参数值的类型相同。

如果 VT 是分配给常量的值的类型,DT 是常量、字典成员或可选参数本身的类型,那么这些类型必须 兼容,兼容的情况是 DTVT 相同, 或者 DT 是其 内部类型VT可为空类型

常量 与出现在它们上的特定 接口回调接口 的实例无关。是否在实例上暴露 常量 取决于语言绑定。

然而,JavaScript 语言绑定允许通过实现了声明 常量 的 IDL 接口 的对象访问常量。 例如,以下 IDL:

[Exposed=Window]
  interface A {
    const short rambaldi = 47;
  };
  

在 JavaScript 中可以通过 A.rambaldiinstanceOfA.rambaldi 访问常量值。

以下扩展属性适用于常量: [CrossOriginIsolated], [Exposed] 和 [SecureContext]。

Const ::
      const ConstType identifier = ConstValue ;
  
ConstValue ::
      布尔字面量
      浮点字面量
      整数
  
布尔字面量 ::
      true
      false
  
浮点字面量 ::
      小数
      -Infinity
      Infinity
      NaN
  
ConstType ::
      原始类型
      标识符
  

以下 IDL 片段 演示了如何定义上述类型的 常量

[Exposed=Window]
  interface Util {
    const boolean DEBUG = false;
    const octet LF = 10;
    const unsigned long BIT_MASK = 0x0000fc00;
    const double AVOGADRO = 6.022e23;
  };
  

2.5.2. 属性

属性 是一个 接口成员命名空间成员(对应 inherit AttributeReststatic OptionalReadOnly AttributeReststringifier OptionalReadOnly AttributeRest, 或 AttributeRest), 用于声明具有给定类型和 标识符 的数据字段,其值可以被检索,并且(在某些情况下)可以更改。属性有两种类型:

  1. 常规属性,用于声明实现 接口 的对象将拥有带有给定 标识符 的数据字段成员。

    interface interface_identifier {
        attribute type identifier;
      };
      
  2. 静态属性,用于声明与实现接口的特定对象无关的属性。

    interface interface_identifier {
        static attribute type identifier;
      };
      

如果属性没有 static 关键字,则声明一个 常规属性。否则,它声明为 静态属性。请注意,除了作为 接口成员 外,只读常规属性 也可以是 命名空间成员

属性 attrgetter 步骤 应以类似“attr getter 步骤如下:”的文字介绍,后跟列表,或以“attr getter 步骤是”开头,后跟内联描述。

属性 attrsetter 步骤 应以类似“attr setter 步骤如下:”的文字介绍,后跟列表,或以“attr setter 步骤是”开头,后跟内联描述。

注意: 定义 getter 步骤 时,隐式访问 this。 定义 setter 步骤 时,隐式访问 this给定值

属性的 标识符不得与相同 接口 上定义的另一个 接口成员 的标识符相同。 静态属性的标识符不得为 "prototype"。

属性的类型由出现在 attribute 关键字之后的类型(对应 Type)给出。 如果 Type标识符 或后接 ? 的标识符, 则标识符必须标识一个 接口枚举回调函数回调接口typedef

在解析 typedef 后,属性的类型不能是以下任何类型的 可为空 或不可为空的版本:

如果在 attribute 关键字之前使用 readonly 关键字,则属性是 只读 的。 实现了定义了只读属性的接口的对象不允许对该属性赋值。 是否仅由语言禁止赋值,忽略赋值还是抛出异常,取决于语言绑定。

interface interface_identifier {
    readonly attribute type identifier;
  };
  

类型为 Promise 类型 的属性必须是 只读 的。此外,它们不能拥有 以下任何 扩展属性:[LegacyLenientSetter], [PutForwards], [Replaceable], 或 [SameObject]。

只读常规属性 可以声明为从祖先接口继承其 getter。 这可以用于使祖先接口中的只读属性在派生接口上可写。 如果声明中包含 inherit,则属性 继承其 getter。 继承其 getter 的属性是与定义继承属性的接口最接近的祖先接口上具有相同标识符的属性。 被继承 getter 的属性必须与继承属性的类型相同。

注意: 语法确保 inherit 不会出现在 只读 属性或 静态属性 上。

[Exposed=Window]
  interface Ancestor {
    readonly attribute TheType theIdentifier;
  };
  
  [Exposed=Window]
  interface Derived : Ancestor {
    inherit attribute TheType theIdentifier;
  };
  

当在 常规属性 声明中使用 stringifier 关键字时,它表明实现接口的对象将被转换为该属性的值。 详情见 § 2.5.5 Stringifiers

interface interface_identifier {
    stringifier attribute DOMString identifier;
  };
  

以下 扩展属性 适用于常规和静态属性: [CrossOriginIsolated], [Exposed], [SameObject], 和 [SecureContext]。

以下 扩展属性 仅适用于常规属性: [LegacyLenientSetter], [LegacyLenientThis], [PutForwards], [Replaceable], [LegacyUnforgeable]。

ReadOnlyMember ::
      readonly ReadOnlyMemberRest
  
ReadOnlyMemberRest ::
      AttributeRest
      MaplikeRest
      SetlikeRest
  
ReadWriteAttribute ::
      AttributeRest
  
InheritAttribute ::
      inherit AttributeRest
  
AttributeRest ::
      attribute TypeWithExtendedAttributes AttributeName ;
  
AttributeName ::
      AttributeNameKeyword
      identifier
  
AttributeNameKeyword ::
      async
      required
  
OptionalReadOnly ::
      readonly
      ε
  

以下 IDL 片段 演示了如何在 接口 上声明 属性

[Exposed=Window]
  interface Animal {
  
    // 一个可以设置为任何字符串值的简单属性。
    readonly attribute DOMString name;
  
    // 一个属性,其值可以被分配。
    attribute unsigned short age;
  };
  
  [Exposed=Window]
  interface Person : Animal {
  
    // 一个从 Animal 继承 getter 行为的属性,不需要在 Person 的描述中指定。
    inherit attribute DOMString name;
  };
  

2.5.3. 操作

一个操作是一个接口成员回调接口成员命名空间成员(匹配 static RegularOperationstringifierRegularOperationSpecialOperation), 它定义了一种可以在实现该接口的对象上调用的行为。 操作有三种类型:

  1. 常规操作, 用于声明实现 接口 的对象将拥有具有给定 标识符 的方法。

    interface interface_identifier {
        return_type identifier(/* arguments... */);
      };
      
  2. 特殊操作, 用于在实现接口的对象上声明特殊行为,例如对象索引和字符串化

    interface interface_identifier {
        /* special_keyword */ return_type identifier(/* arguments... */);
        /* special_keyword */ return_type (/* arguments... */);
      };
      
  3. 静态操作, 用于声明与实现接口的特定对象无关的操作。

    interface interface_identifier {
        static return_type identifier(/* arguments... */);
      };
      

如果一个操作有标识符但没有 static 关键字,则声明一个 常规操作。 如果操作的声明中使用了 特殊关键字(即,任何匹配 Special 的关键字,或 stringifier 关键字), 则声明为特殊操作。一个操作可以同时声明为常规操作和特殊操作; 详情见 § 2.5.6 特殊操作。 请注意,除了作为 接口成员 外, 常规操作也可以是 回调接口成员命名空间成员

如果操作没有标识符, 则必须使用其中一个特殊关键字将其声明为特殊操作。

常规操作静态操作 的标识符不得与在相同 接口回调接口命名空间 上定义的 常量属性 的标识符相同。 静态操作的标识符不得为 "prototype"。

注意: 然而,标识符可以与接口上其他操作的标识符相同。 这是指定操作重载的方式。

注意: 静态操作 的标识符可以与在相同 接口 上定义的 常规操作 的标识符相同。

操作的 返回类型 由 出现在操作的可选 标识符 之前的类型(对应 Type)给出。 如果返回类型是后接 ?标识符, 则该标识符必须标识 接口字典枚举回调函数回调接口typedef

操作的参数(对应 ArgumentList) 在声明中的括号内给出。每个参数都由一个类型(对应 Type)后跟一个 标识符(对应 ArgumentName)指定。

注意: 为了表达的灵活性,操作参数的标识符还可以指定为 匹配 ArgumentNameKeyword 符号的关键字之一,无需转义。

如果操作参数的 Type 是后接 ? 的标识符, 则该标识符必须标识 接口枚举回调函数回调接口, 或 typedef。 如果操作参数的类型是未后接 ?标识符,则该标识符必须标识其中任何一个定义,或 字典

如果操作参数的类型在解析 typedef 后, 是 可为空类型, 则其 内部类型 不能是 字典类型

interface interface_identifier {
    return_type identifier(type identifier, type identifier /* , ... */);
  };
  

每个参数的标识符不得与相同操作声明中的其他参数标识符相同。

每个参数可以有一个前置的 扩展属性 列表(对应 ExtendedAttributeList), 用于控制传递给语言绑定的参数值的处理方式。

interface interface_identifier {
    return_type identifier([extended_attributes] type identifier, [extended_attributes] type identifier /* , ... */);
  };
  

以下 IDL 片段 演示了如何在 接口 上声明 常规操作

[Exposed=Window]
  interface Dimensions {
    attribute unsigned long width;
    attribute unsigned long height;
  };
  
  [Exposed=Window]
  interface Button {
  
    // 一个不带参数并返回布尔值的操作。
    boolean isMouseOver();
  
    // 重载的操作。
    undefined setDimensions(Dimensions size);
    undefined setDimensions(unsigned long width, unsigned long height);
  };
  

操作或 构造器操作 被视为 可变参数,如果最后一个参数使用 ... 标记紧跟在参数类型之后。声明操作为可变参数表示在调用该操作时可以在最后一个参数之后传递任意数量的参数。那些额外的隐含形式参数与操作声明中的最后一个显式参数类型相同。在调用操作时,最后一个参数也可以省略。操作的参数声明中,除非是最后一个参数,否则不得使用 ... 标记。

interface interface_identifier {
    return_type identifier(type... identifier);
    return_type identifier(type identifier, type... identifier);
  };

扩展属性 使用参数列表的情况(例如 [LegacyFactoryFunction])和 回调函数,当其参数列表中使用 ... 标记时,也视为 可变参数

以下 IDL 片段 定义了具有两个可变参数操作的接口:

[Exposed=Window]
  interface IntegerSet {
    readonly attribute unsigned long cardinality;
  
    undefined union(long... ints);
    undefined intersection(long... ints);
  };
  

在 JavaScript 绑定中,可变参数操作通过可以接受后续参数的函数来实现:

var s = getIntegerSet();  // 获取 IntegerSet 的实例。
  
  s.union();                // 不传递与 'ints' 对应的参数。
  s.union(1, 4, 7);         // 传递三个与 'ints' 对应的参数。
  

对于不支持可变参数函数的语言绑定,可以指定传递一个显式数组或整数列表给这样的操作。

如果参数使用了 optional 关键字,则被认为是 可选参数。可变参数操作的最后一个参数也被视为可选参数。声明一个参数为可选表示在调用操作时可以省略该参数的值。可变参数操作的最后一个参数不能明确声明为可选。

interface interface_identifier {
    return_type identifier(type identifier, optional type identifier);
  };

可选参数还可以有 默认值。如果参数的标识符后跟 U+003D (=) 和一个值(对应 DefaultValue),则该值为该可选参数的 默认值。可变参数操作的隐式可选最后一个参数不能指定默认值。当调用操作时,如果省略了相应的参数,则将假定使用默认值。

interface interface_identifier {
    return_type identifier(type identifier, optional type identifier = "value");
  };

强烈建议不要为 boolean 类型的参数使用默认值 true,因为这可能会让作者感到困惑,他们可能期望默认转换 undefined 被使用(即 false)。[API-DESIGN-PRINCIPLES]

如果参数的类型是 字典类型联合类型,且其具有 字典类型 作为其 展平的成员类型之一,并且该字典类型及其祖先没有 必需 成员,并且该参数是最后一个参数或后面仅跟有 可选参数,则该参数必须指定为可选并提供默认值。

这鼓励 API 设计不要求作者传递一个空字典值,只希望使用字典的默认值。

通常提供的默认值将是 {},但在一种情况下,即 联合类型 拥有字典类型作为其 展平的成员类型之一,默认值可以初始化联合类型的另一个成员。

当布尔文字标记(truefalse),null 标记,一个 整数 标记,一个 小数 标记或三个特殊浮点文字值之一(Infinity-InfinityNaN)用作 默认值 时,其解释方式与 常量 的解释方式相同。

undefined 标记被用作 默认值 时,其值为 IDL undefined 值。

可选参数默认值还可以使用 字符串 标记指定,其 是如下所述的 字符串类型
  1. S 为与 字符串 标记匹配的 标量值 序列,并移除其前后 U+0022 (") 标量值

  2. 根据参数的类型:

    DOMString
    USVString
    一个 枚举类型

    字符串 标记的值为 S

    ByteString

    断言:S 不包含任何高于 U+00FF 的 码点

    字符串 标记的值是 S同构编码

如果 可选参数 的类型是 枚举类型,则其 默认值,如果指定,必须是 枚举的值之一。

可选参数的默认值还可以使用两个标记值 [] 指定,表示空序列值。该值的类型与用作其默认值的可选参数的类型相同。该类型必须是 序列类型,一个 可空类型,其 内部类型序列类型,或是 联合类型 或具有 可空联合类型,且其中一个展平成员类型是 序列类型

可选参数默认值也可以使用双标记值 {} 来指定,它表示一个默认初始化的(如同来自 ES null 或没有属性的对象)字典值。此值的类型与其用作默认值的可选参数的类型相同。该类型必须是一个字典类型,或一个在其扁平化成员类型中具有字典类型联合类型

以下 IDL 片段 定义了一个具有单个可以用两种不同参数列表长度调用的 操作接口

[Exposed=Window]
  interface ColorCreator {
    object createColor(double v1, double v2, double v3, optional double alpha);
  };
  

这等同于一个具有两个 重载 操作接口

[Exposed=Window]
  interface ColorCreator {
    object createColor(double v1, double v2, double v3);
    object createColor(double v1, double v2, double v3, double alpha);
  };
  

以下 IDL 片段 定义了一个带有字典参数的 接口

dictionary LookupOptions {
    boolean caseSensitive = false;
  };
  
  [Exposed=Window]
  interface AddressBook {
    boolean hasAddressForName(USVString name, optional LookupOptions options = {});
  };
  

如果 hasAddressForName 只用一个参数调用,第二个参数将是一个默认初始化的 LookupOptions 字典,这将使 caseSensitive 设置为 false

以下扩展属性适用于操作:[CrossOriginIsolated],[Default],[Exposed],[LegacyUnforgeable],[NewObject],和 [SecureContext]。

一个操作 operation方法步骤 应使用如下格式的文本引入:“operation(arg1, arg2, ...) 方法步骤如下:”后跟一个列表,或者“operation(arg1, arg2, ...) 方法步骤是为了”后跟一个内联描述。

注意:在定义 方法步骤 时,隐式地可以访问 this

DefaultValue ::
      ConstValue
      string
      [ ]
      { }
      null
      undefined
  
Operation ::
      RegularOperation
      SpecialOperation
  
RegularOperation ::
      Type OperationRest
  
SpecialOperation ::
      Special RegularOperation
  
Special ::
      getter
      setter
      deleter
  
OperationRest ::
      OptionalOperationName ( ArgumentList ) ;
  
OptionalOperationName ::
      OperationName
      ε
  
OperationName ::
      OperationNameKeyword
      identifier
  
OperationNameKeyword ::
      includes
  
ArgumentList ::
      Argument Arguments
      ε
  
Arguments ::
      , Argument Arguments
      ε
  
Argument ::
      ExtendedAttributeList ArgumentRest
  
ArgumentRest ::
      optional TypeWithExtendedAttributes ArgumentName Default
      Type Ellipsis ArgumentName
  
ArgumentName ::
      ArgumentNameKeyword
      identifier
  
Ellipsis ::
      ...
      ε
  
ArgumentNameKeyword ::
      async
      attribute
      callback
      const
      constructor
      deleter
      dictionary
      enum
      getter
      includes
      inherit
      interface
      iterable
      maplike
      mixin
      namespace
      partial
      readonly
      required
      setlike
      setter
      static
      stringifier
      typedef
      unrestricted
  
2.5.3.1. toJSON

通过声明一个 toJSON 常规操作, 一个 接口 指定了如何将实现它的对象转换为 JSON 类型

toJSON 常规操作 保留用于此用途。 它必须不带参数并返回 JSON 类型

JSON 类型包括:

toJSON 常规操作 在语言绑定中如何在对象上可用,以及 JSON 类型 如何精确地转换为 JSON 字符串,取决于具体的语言绑定。

注意:在 JavaScript 语言绑定中,这通过暴露一个 toJSON 方法来实现, 该方法返回转换为 JavaScript 值的 JSON 类型, 该值可以通过 JSON.stringify() 函数转化为 JSON 字符串。 此外,在 JavaScript 语言绑定中, toJSON 操作可以带有 [Default] 扩展属性, 在这种情况下,默认的 toJSON 步骤 将被暴露。

以下 IDL 片段 定义了一个名为 Transaction 的接口, 它有一个以行文方式定义的 toJSON 方法:

[Exposed=Window]
  interface Transaction {
    readonly attribute DOMString from;
    readonly attribute DOMString to;
    readonly attribute double amount;
    readonly attribute DOMString description;
    readonly attribute unsigned long number;
    TransactionJSON toJSON();
  };
  
  dictionary TransactionJSON {
    Account from;
    Account to;
    double amount;
    DOMString description;
  };
  

Transaction 接口toJSON 常规操作 可以定义如下:

toJSON() 方法步骤如下:

  1. json 为一个新的 映射

  2. 对于 « "from", "to", "amount", "description" » 中的每个属性 标识符 attr

    1. this 上运行 attrgetter 步骤,并将结果赋值给 value

    2. json[attr] 设置为 value

  3. 返回 json

在 JavaScript 语言绑定中, Transaction 对象上将存在一个 toJSON() 方法:

// 获取一个 Transaction 实例。
  var txn = getTransaction();

  // 结果评估为类似这样的对象:
  // {
  //   from: "Bob",
  //   to: "Alice",
  //   amount: 50,
  //   description: "books"
  // }
  txn.toJSON();

  // 结果评估为类似这样的字符串:
  // '{"from":"Bob","to":"Alice","amount":50,"description":"books"}'
  JSON.stringify(txn);
  

2.5.4. 构造操作

如果一个 接口 具有 构造操作 成员(匹配 Constructor),这表明可以使用 构造函数 创建实现该 接口 的对象。

在一个 接口 上可以出现多个 构造操作。 对于该 接口 上的每个 构造操作,将有一种方式通过传递指定的参数来尝试构造实例。

名为 interface 的接口的构造操作的 构造步骤应使用如下形式的文本引入:“new interface(arg1, arg2, ...) 构造步骤如下:”,然后跟一个列表,或者“new interface(arg1, arg2, ...) 构造步骤是”后跟内联描述。

构造步骤必须不做任何操作,初始化作为 this 传递的值,或者抛出异常。

如果构造函数没有初始化 this,可以写作:“new Example(init) 构造步骤是不做任何操作。”

有关如何实现 构造操作 的详细信息,请参见 § 3.7.1 接口对象

以下 IDL 定义了两个接口。第二个接口具有 构造操作,而第一个没有。

[Exposed=Window]
  interface NodeList {
    Node item(unsigned long index);
    readonly attribute unsigned long length;
  };
  
  [Exposed=Window]
  interface Circle {
    constructor();
    constructor(double radius);
    attribute double r;
    attribute double cx;
    attribute double cy;
    readonly attribute double circumference;
  };
  

支持这些接口的 JavaScript 实现将在 Circle 接口对象上实现一个 [[Construct]] 内部方法,该方法将返回实现该接口的 新对象。它可以接受零个或一个参数。

目前尚不清楚 NodeList 接口对象是否会实现 [[Construct]] 内部方法。无论如何,尝试将其用作构造函数都会导致抛出 TypeError[whatwg/webidl 问题 #698]

var x = new Circle();      // 这使用零参数构造函数创建了
                             // 一个实现 Circle 接口的平台对象的引用。

  var y = new Circle(1.25);  // 这次使用一个参数构造函数创建了一个 Circle 对象。

  var z = new NodeList();    // 这会抛出一个 TypeError,因为没有
                             // 声明构造函数。
  
Constructor ::
      constructor ( ArgumentList ) ;
  
ArgumentList ::
      Argument Arguments
      ε
  
Arguments ::
      , Argument Arguments
      ε
  
Argument ::
      ExtendedAttributeList ArgumentRest
  
ArgumentRest ::
      optional TypeWithExtendedAttributes ArgumentName Default
      Type Ellipsis ArgumentName
  
ArgumentName ::
      ArgumentNameKeyword
      identifier
  
Ellipsis ::
      ...
      ε
  
ArgumentNameKeyword ::
      async
      attribute
      callback
      const
      constructor
      deleter
      dictionary
      enum
      getter
      includes
      inherit
      interface
      iterable
      maplike
      mixin
      namespace
      partial
      readonly
      required
      setlike
      setter
      static
      stringifier
      typedef
      unrestricted
  

2.5.5. 字符串化

当一个 接口 具有 stringifier 时, 这表示实现该接口的对象具有非默认的字符串转换。Stringifier 可以通过使用 stringifier 关键字来指定,当单独使用时,会创建一个字符串化操作。

interface interface_identifier {
    stringifier;
  };
  

与接口相关的文本必须定义接口的 字符串化行为

stringifier 关键字也可以放置在 属性 上。 在这种情况下,将对象转换为字符串的值是该属性的值。stringifier 关键字不得放置在属性上,除非 它被声明为类型 DOMStringUSVString。 它也不得放置在 静态属性 上。

interface interface_identifier {
    stringifier attribute DOMString identifier;
  };
  

在给定的 接口 上,最多只能存在一个 stringifier。

Stringifier ::
      stringifier StringifierRest
  
StringifierRest ::
      OptionalReadOnly AttributeRest
      ;
  

以下 IDL 片段 定义了一个接口,该接口将字符串化为其 name 属性的值:

[Exposed=Window]
  interface Student {
    constructor();
    attribute unsigned long id;
    stringifier attribute DOMString name;
  };
  

在 JavaScript 绑定中,在需要字符串的上下文中使用 Student 对象将导致使用对象的 name 属性的值:

var s = new Student();
  s.id = 12345678;
  s.name = '周杰倫';

  var greeting = 'Hello, ' + s + '!';  // 现在 greeting == 'Hello, 周杰倫!'。
  

以下 IDL 片段 定义了一个接口,该接口具有自定义字符串化行为,且 IDL 本身未指定。

[Exposed=Window]
  interface Student {
    constructor();
    attribute unsigned long id;
    attribute DOMString? familyName;
    attribute DOMString givenName;

    stringifier;
  };
  

因此,需要有相应的文本来解释字符串化行为。 我们假设 familyNamegivenName 属性分别由姓氏和名字概念支持。

字符串化行为 步骤如下:

  1. 如果的姓氏为 null, 则返回的名字。

  2. 返回的名字,后跟 U+0020 空格,再后跟的姓氏的串联。

IDL 的 JavaScript 实现行为如下:

var s = new Student();
  s.id = 12345679;
  s.familyName = 'Smithee';
  s.givenName = 'Alan';

  var greeting = 'Hi ' + s;  // 现在 greeting == 'Hi Alan Smithee'。
  

2.5.6. 特殊操作

特殊操作 是在实现接口的对象上声明某种特殊行为的声明,这些特殊操作声明出现在该接口上。通过在操作声明中使用 特殊关键字 来声明特殊操作。

特殊操作有三种类型。下表列出了给定的特殊操作类型所使用的特殊关键字以及该特殊操作的目的:

特殊操作 关键字 目的
取值器 getter 定义对象在进行属性检索时的行为。
赋值器 setter 定义对象在进行属性赋值或创建时的行为。
删除器 deleter 定义对象在进行属性删除时的行为。

并非所有语言绑定都支持所有这四种特殊对象行为。当使用没有标识符的操作声明特殊操作时,在不支持这些特殊操作的语言绑定中,此类功能将不会被实现。

以下 IDL 片段定义了一个具有 getter 和 setter 的接口:

[Exposed=Window]
  interface Dictionary {
    readonly attribute unsigned long propertyCount;
  
    getter double (DOMString propertyName);
    setter undefined (DOMString propertyName, double propertyValue);
  };
  

在不支持属性 getter 和 setter 的语言绑定中,实现 Dictionary 的对象将不会具备该特殊行为。

定义一个带有 标识符 的特殊操作等同于将该特殊操作分离为独立的、没有标识符的声明。这种方法被允许简化接口操作的 方法步骤

以下两个接口是等价的:

[Exposed=Window]
  interface Dictionary {
    readonly attribute unsigned long propertyCount;
  
    getter double getProperty(DOMString propertyName);
    setter undefined setProperty(DOMString propertyName, double propertyValue);
  };
  
[Exposed=Window]
  interface Dictionary {
    readonly attribute unsigned long propertyCount;
  
    double getProperty(DOMString propertyName);
    undefined setProperty(DOMString propertyName, double propertyValue);
  
    getter double (DOMString propertyName);
    setter undefined (DOMString propertyName, double propertyValue);
  };
  

一个给定的 特殊关键字不能在一个操作上出现两次。

取值器和赋值器有两种类型:一种是使用 DOMString 作为属性名,称为 命名属性取值器命名属性赋值器,另一种是使用 unsigned long 作为属性索引,称为 索引属性取值器索引属性赋值器。删除器只有一种类型:命名属性删除器。详情请参阅 § 2.5.6.1 索引属性§ 2.5.6.2 命名属性

在给定的 接口 上,最多只能有一个 命名属性删除器,以及每种类型的取值器和赋值器各一个。

如果一个接口具有某种类型的赋值器,则它必须也具有该类型的取值器。如果它具有 命名属性删除器,则它必须也具有 命名属性取值器

使用操作声明的特殊操作不得是 可变参数,也不得有任何 可选参数

如果一个对象实现了多个定义相同特殊操作的 接口,则无法确定将调用哪个(如果有的话)特殊操作。

2.5.6.1. 索引属性

定义了 索引属性取值器接口 被认为 支持索引属性。 由此扩展,如果 平台对象 实现了 接口,则该对象被认为 支持索引属性

如果一个接口 支持索引属性,那么该接口定义必须附带描述对象在任何给定时间可以使用哪些索引来进行索引操作的描述。这些索引称为 支持的属性索引

支持索引属性的接口必须定义一个名为 "length" 的 整数类型 属性

索引属性取值器必须声明为接受一个 unsigned long 参数。 索引属性赋值器必须声明为接受两个参数,其中第一个参数是 unsigned long

interface interface_identifier {
    getter type identifier(unsigned long identifier);
    setter type identifier(unsigned long identifier, type identifier);
  
    getter type (unsigned long identifier);
    setter type (unsigned long identifier, type identifier);
  };
  

以下要求适用于索引属性取值器和赋值器的定义:

请注意,如果使用带有标识符的 操作 指定了 索引属性取值器赋值器,那么使用不是 支持的属性索引 的整数对对象进行索引时的行为不一定与使用该索引调用操作时的行为相同。在这种情况下的实际行为取决于具体的语言绑定。

在 JavaScript 语言绑定中,进行的是常规的属性查找。例如,以下 IDL:

[Exposed=Window]
  interface A {
    getter DOMString toWord(unsigned long index);
  };
  

假设实现 A 的对象在 0 ≤ index < 2 的范围内具有 支持的属性索引。还假设 toWord 定义为将其参数转换为英文单词。当使用超出范围的索引调用 操作 时的行为与直接对对象进行索引的行为不同:

var a = getA();
  
  a.toWord(0);  // 返回 "zero".
  a[0];         // 也返回 "zero".
  
  a.toWord(5);  // 返回 "five".
  a[5];         // 返回 undefined,因为没有名为 "5" 的属性。
  

以下 IDL 片段定义了一个 OrderedMap 接口,该接口允许通过名称或索引号检索和设置值:

[Exposed=Window]
  interface OrderedMap {
    readonly attribute unsigned long size;
  
    getter any getByIndex(unsigned long index);
    setter undefined setByIndex(unsigned long index, any value);
  
    getter any get(DOMString name);
    setter undefined set(DOMString name, any value);
  };
  

由于所有特殊操作都是通过带有标识符的操作声明的,因此唯一需要的附加说明是描述这些集合具有哪些键。假设 get() 操作定义为在尝试查找 OrderedMap 中不存在的条目时返回 null,那么以下两句话就足够了:

实现 OrderedMap 的对象 map 支持范围为 0 ≤ index < map.size 的索引属性。

此类对象还支持每个名称属性,对于这些名称,若将其传递给 get(),将返回非空值。

§ 3.9 传统平台对象 中所述,JavaScript 实现将在实现 OrderedMap传统平台对象 上创建对应于命名和索引属性集的属性。这些属性可以像调用对象的方法一样与对象交互,如下所示:

// 假设 map 是一个实现了 OrderedMap 接口的传统平台对象。
  var map = getOrderedMap();
  var x, y;

  x = map[0];       // 如果 map.length > 0,则等效于:
                    //
                    //   x = map.getByIndex(0)
                    //
                    // 因为一个名为 "0" 的属性将被添加到 map 上。
                    // 否则,x 将被设置为 undefined,因为 map 上没有名为 "0" 的属性。

  map[1] = false;   // 这将等效于:
                    //
                    //   map.setByIndex(1, false)

  y = map.apple;    // 如果存在名为 "apple" 的属性,则这将等效于:
                    //
                    //   y = map.get('apple')
                    //
                    // 因为一个名为 "apple" 的属性将被添加到 map 上。否则,y 将被设置为 undefined,因为 map 上没有名为 "apple" 的属性。

  map.berry = 123;  // 这将等效于:
                    //
                    //   map.set('berry', 123)

  delete map.cake;  // 如果存在名为 "cake" 的属性,则将删除 "cake" 属性,然后执行以下操作的等效操作:
                    //
                    //   map.remove("cake")
  
2.5.6.2. 命名属性

定义了 命名属性 getter接口 称为 支持命名属性。 拓展来说,实现了 接口平台对象 也称为 支持命名属性

如果一个接口 支持命名属性, 那么该接口的定义必须附带描述该对象在任何给定时间可用于索引的名称的有序集合。 这些名称称为 受支持的属性名称

命名属性 getter 和 deleter 必须声明只接受一个 DOMString 参数。 命名属性 setter 必须声明接受两个参数,其中第一个是 DOMString

interface interface_identifier {
    getter type identifier(DOMString identifier);
    setter type identifier(DOMString identifier, type identifier);
    deleter type identifier(DOMString identifier);
  
    getter type (DOMString identifier);
    setter type (DOMString identifier, type identifier);
    deleter type (DOMString identifier);
  };
  

以下要求适用于命名属性 getter、setter 和 deleter 的定义:

注意:索引属性 类似, 如果 命名属性 gettersetterdeleter 是使用带有 标识符操作 指定的, 那么使用不属于 受支持的属性名称 的名称对对象进行索引的行为不一定与调用该名称的操作时的行为相同;此行为是语言绑定特定的。

2.5.7. 静态属性和操作

静态属性静态操作 是那些 不与声明其上的 接口 的特定实例相关联的,而是与接口本身相关联的属性和操作。静态属性和操作通过在声明中使用 static 关键字来声明。

是否可以通过接口实例的引用调用静态操作或获取或设置静态属性取决于语言绑定的具体实现。

StaticMember ::
  static StaticMemberRest
StaticMemberRest ::
  OptionalReadOnly AttributeRest
  RegularOperation

以下 IDL 片段 定义了一个具有静态操作的接口 Circle

[Exposed=Window]
  interface Point { /* ... */ };
  
  [Exposed=Window]
  interface Circle {
    attribute double cx;
    attribute double cy;
    attribute double radius;
  
    static readonly attribute long triangulationCount;
    static Point triangulate(Circle c1, Circle c2, Circle c3);
  };
  

在 JavaScript 语言绑定中,函数对象 triangulate 和访问器属性 triangulationCount 将存在于 Circle接口对象 上:

var circles = getCircles();           // Circle 对象的数组
  
  typeof Circle.triangulate;            // 计算结果为 "function"
  typeof Circle.triangulationCount;     // 计算结果为 "number"
  Circle.prototype.triangulate;         // 计算结果为 undefined
  Circle.prototype.triangulationCount;  // 计算结果同样为 undefined
  circles[0].triangulate;               // 也是 undefined
  circles[0].triangulationCount;        // 也是 undefined
  
  // 调用静态操作
  var triangulationPoint = Circle.triangulate(circles[0], circles[1], circles[2]);
  
  // 查找我们已经完成了多少次三角剖分
  window.alert(Circle.triangulationCount);
  

2.5.8. 重载

如果一个定义在 接口 上的 常规操作静态操作标识符 与该接口上的其他同类操作(常规或静态)的标识符相同, 则该操作被称为 重载。 当使用重载操作的标识符调用实现该接口的对象上的操作时,传递给操作的参数数量和类型决定了实际调用的重载操作。 构造函数操作 也可以被重载。 对于重载操作和构造函数可以指定的参数有一些限制,为了描述这些限制,使用了“有效重载集”的概念。

一组重载的 操作 必须满足以下条件之一:

操作 不能在 接口部分接口接口混入部分接口混入 定义之间被重载。

例如,fg 的重载都是不允许的:

[Exposed=Window]
  interface A {
    undefined f();
  };
  
  partial interface A {
    undefined f(double x);
    undefined g();
  };
  
  partial interface A {
    undefined g(DOMString x);
  };
  

注意,构造函数操作 和 [LegacyFactoryFunction] 扩展属性 不允许出现在 部分接口定义中, 因此不需要另外禁止构造函数的重载。

一个 有效重载集 表示特定 操作、构造函数(由 构造函数操作 或 [LegacyFactoryFunction] 指定)或 回调函数 的允许调用。 用于 计算有效重载集 的算法 针对以下四种 IDL 构造之一进行操作,下面列出的是计算集时需要的输入:

对于常规操作
对于静态操作
对于构造函数
对于遗留工厂函数

一个有效的重载集用于确定在接口上指定的重载操作和构造函数是否存在歧义等。

有效重载集项目是形式为 (可调用, 类型列表, 可选性列表) 的元组,其项目描述如下:

每个 元组 代表操作、构造函数或回调函数的一个允许调用, 其参数值列表为给定类型。由于使用了 可选参数可变参数 操作和构造函数,有效重载集 中可能有多个项标识相同的操作或构造函数。

以下算法描述了如何 计算有效重载集。 如果需要,使用以下输入变量:

当提到扩展属性的参数时,它指的是扩展属性的 命名参数列表 中的一个参数。

  1. S 为一个 有序集合

  2. F 为一个 有序集合,其中包含以下项, 根据 有效重载集 的种类:

    对于常规操作

    F 的元素是接口 I 上具有标识符 A常规操作

    对于静态操作

    F 的元素是接口 I 上具有标识符 A静态操作

    对于构造函数

    F 的元素是接口 I 上的 构造函数操作

    对于遗留工厂函数

    F 的元素是接口 I 上的带有 [LegacyFactoryFunction] 扩展属性,其命名参数列表的标识符为 A

  3. maxargF 中操作、遗留工厂函数或回调函数声明的最大参数数。 对于 可变参数 的操作和遗留工厂函数, 省略号出现的参数计为一个参数。

    注意:因此 undefined f(long x, long... y); 被认为声明了两个参数。

  4. maxmax(maxarg, N)。

  5. 对每个 F 中的操作或扩展属性 X

    1. arguments列表,其中包含 X 声明的参数。

    2. narguments大小

    3. types 为一个 类型列表

    4. optionalityValues 为一个 可选性列表

    5. 对每个 arguments 中的参数:

      1. 参数的类型追加到 types

      2. 如果 参数是最后一个可变参数,则追加 "variadic" 到 optionalityValues; 如果参数是 可选的,则追加 "optional"; 否则追加 "required"。

    6. 元组 (X, types, optionalityValues) 追加到 S

    7. 如果 X 被声明为 可变参数,则:

      1. 对每个 nmax − 1 范围内的 i

        1. t 为一个 类型列表

        2. o 为一个 可选性列表

        3. 0 到 n − 1 范围内的每个 j

          1. types[j] 追加到 t

          2. optionalityValues[j] 追加到 o

        4. ni 范围内的每个 j

          1. types[n − 1] 追加到 t

          2. 追加 "variadic" 到 o

        5. 元组 (X, t, o) 追加到 S

    8. in − 1。

    9. i ≥ 0 时:

      1. 如果 arguments[i] 不是 可选的 (即它未标记为“可选”且不是最后一个可变参数),则 中断

      2. t 为一个 类型列表

      3. o 为一个 可选性列表

      4. 对每个 0 到 i − 1 范围内的 j

        1. types[j] 追加到 t

        2. optionalityValues[j] 追加到 o

      5. 元组 (X, t, o) 追加到 S

        注意: 如果 i 为 0,这意味着要将元组 (X, « », « ») (其中 "« »" 表示一个 空列表)添加到 S

      6. i 设为 i − 1。

  6. 返回 S

对于以下接口:

[Exposed=Window]
  interface A {
    /* f1 */ undefined f(DOMString a);
    /* f2 */ undefined f(Node a, DOMString b, double... c);
    /* f3 */ undefined f();
    /* f4 */ undefined f(Event a, DOMString b, optional DOMString c, double... d);
  };
  

假设 NodeEvent 是两个接口,且没有对象可以同时实现这两个接口,有效重载集中与标识符 f 和参数数量 4 对应的常规操作为:

«
    (f1, « DOMString »,                           « required »),
    (f2, « Node, DOMString »,                     « required, required »),
    (f2, « Node, DOMString, double »,             « required, required, variadic »),
    (f2, « Node, DOMString, double, double »,     « required, required, variadic, variadic »),
    (f3, « »,                                     « »),
    (f4, « Event, DOMString »,                    « required, required »),
    (f4, « Event, DOMString, DOMString »,         « required, required, optional »),
    (f4, « Event, DOMString, DOMString, double », « required, required, optional, variadic »)
  »
  

两个类型在以下算法返回 true 时被认为是可区分的

  1. 如果一个类型包含一个可空类型,另一个类型也包含一个可空类型,是一个联合类型,且展平后的成员类型包含字典类型,或者是字典类型,则返回false

    以下类型对不可区分:
  2. 如果两个类型都是联合类型可空联合类型,则每个成员类型相互可区分时返回 true,否则返回 false

  3. 如果一个类型是联合类型或可空联合类型,返回 true,如果联合类型的每个成员类型与非联合类型相互可区分,否则返回 false

  4. 考虑两个类型的“最内层”类型,通过获取每个类型的内部类型,如果它是注释类型,然后获取它的内部类型,如果结果是可空类型。如果这两个最内层类型出现在下表中,且在相应条目中有“●”标记,或者有字母且满足下表的附加要求,则返回true,否则返回false

    类别:

    interface-like
    dictionary-like
    sequence-like
    undefined
    boolean
    numeric types
    bigint
    string types
    object
    symbol
    interface-like
    callback function
    dictionary-like
    async iterable
    sequence-like
    undefined
    boolean
    numeric types (b)
    bigint
    string types (d)
    object
    symbol
    interface-like (a)
    callback function (c)
    dictionary-like
    async iterable
    sequence-like
    1. 两个标识的接口类型不同,且没有单一平台对象同时实现这两种接口类型。

    2. 这些类型是可区分的,但对其在重载中的使用有额外限制。另请注意关于使用这些类型联合的建议

    3. 不具有 [LegacyTreatNonObjectAsNull] 扩展属性的回调函数可与字典类类别中的类型区分开。

      例如,当将 ECMAScript 值转换为包含回调函数和字典类类型的联合类型时,如果该值是可调用的, 则将其转换为回调函数。否则,将其转换为字典类类型。
    4. 这些类型是可区分的,但是在从 ECMAScript 值转换时,如果字符串类型也在重载集或联合中,则字符串对象永远不会转换成异步可迭代类型(即使它具有 %Symbol.iterator% 方法)。

    doubleDOMString 是可区分的,因为在数值类型字符串类型的交叉处有一个●。
    doublelong 不可区分,因为它们都是数值类型,且在数值类型之间的交叉处没有●或字母。
    给定:
    callback interface CBIface {
      undefined handle();
    };
    
    [Exposed=Window]
    interface Iface {
      attribute DOMString attr2;
    };
    
    dictionary Dict {
      DOMString field1;
    };
    

    CBIface 可与 Iface 区分开,因为字典类型和接口类型的交叉处有 ●,但不能与 Dict 区分,因为字典类型与自身的交叉处没有 ●。

    Promise 类型没有出现在上表中,因此它不能与任何其他类型区分。

如果在一个有效重载集中有多个具有相同类型列表 大小项目,那么对于这些项目,必须存在一个索引i,使得每对项目在索引i处的类型是可区分的。该最小索引被称为区分参数索引,用于具有给定类型列表大小的有效重载集中的项目。

一个有效重载集中不得包含多个具有相同类型列表 大小项目,其中一个项目在区分参数索引处有一个bigint参数,而另一个项目在该索引处有一个数字类型参数。

考虑在之前示例中显示的有效重载集合。该集合中包含多个具有类型列表大小为2、3和4的项。对于每个类型列表大小,区分参数索引为0,因为NodeEvent可区分的

但是,以下重载的使用是无效的:

[Exposed=Window]
  interface B {
    undefined f(DOMString x);
    undefined f(USVString x);
  };
  

因为DOMStringUSVString不可区分。

此外,对于每个索引j,如果j小于给定类型列表大小的区分参数索引,那么所有项的类型列表中索引j处的类型必须相同,且所有项的可选性列表中索引j处的可选性值也必须相同。

以下是无效的:

[Exposed=Window]
  interface B {
    /* f1 */ undefined f(DOMString w);
    /* f2 */ undefined f(long w, double x, Node y, Node z);
    /* f3 */ undefined f(double w, double x, DOMString y, Node z);
  };
  

对于参数数量为4的情况,有效重载集合为:

«
    (f1, « DOMString »,                       « required »),
    (f2, « long, double, Node, Node »,        « required, required, required, required »),
    (f3, « double, double, DOMString, Node », « required, required, required, required »)
  »

查看类型列表大小为4的项,区分参数索引为2,因为NodeDOMString可区分的。然而,由于这两个重载在索引0处的参数不同,因此此重载是无效的。

2.5.8.1. 重载与联合类型

本节是说明性的。

对于定义IDL 操作的规范来说,重载和 联合类型以及 可选参数之间似乎有一些功能重叠。

首先需要注意的是,重载联合类型可选参数的行为不同,并且一个不能完全用另一个来定义(当然,除非提供了额外的说明文字,但这可能会违背 Web IDL 类型系统的目的)。 例如,考虑在 CanvasDrawPath 接口[HTML]上定义的 stroke() 操作:

interface CanvasDrawPathExcerpt {
    undefined stroke();
    undefined stroke(Path2D path);
  };
  

根据JavaScript语言绑定,调用stroke(undefined)会尝试调用第二个重载,导致抛出 TypeError ,因为undefined不能被转换Path2D。 然而,如果操作改为定义为具有可选参数并合并为一个操作,

interface CanvasDrawPathExcerptOptional {
    undefined stroke(optional Path2D path);
  };
  

重载解析算法将会将path参数视为缺失, 并不会抛出任何异常。

注意: 在这个特定的例子中,后者的行为实际上更符合Web开发人员的预期。 如果今天设计CanvasDrawPath ,将会为stroke()使用可选参数

此外,还有语义上的差异。联合类型通常用于"任何类型都可以以大致相同的方式工作"的场景。 相比之下,重载 操作旨在更好地映射到C++等语言特性,并且通常更适合根据不同参数类型执行实质性不同操作的情况。 然而,在大多数情况下,具有如此实质性差异的操作最好使用不同的名称,以避免Web开发人员的混淆, 因为JavaScript语言不提供语言级的重载。因此,重载很少适用于新的API,而通常出现在旧的API或特殊情况下。

尽管如此,我们仍提供以下建议和示例,以供在决定使用哪种Web IDL语言特性时遇到困难时参考:

当情况不符合上述任何类别时,规范作者可以自行选择样式,因为很可能任何一种样式都能充分且方便地描述预期的行为。然而,联合类型可选参数的定义与转换算法比重载解决算法更容易实现和推理,并且通常会在JavaScript语言绑定中生成更符合惯用法的API。因此,除非有其他考虑,联合类型可选参数或两者都是默认选择。

如果作者认为合适且方便,规范也可以自由混合使用联合类型和重载。

2.5.9. 可迭代声明

一个接口可以通过在接口主体中使用一个可迭代声明(匹配Iterable)来声明为可迭代

interface interface_identifier {
    iterable<value_type>;
    iterable<key_type, value_type>;
  };
  

实现了声明为可迭代接口的对象支持通过迭代获取一系列值。

注意:在JavaScript语言绑定中,一个可迭代的接口将在其接口原型对象上拥有entriesforEachkeysvalues,以及%Symbol.iterator%属性。

如果给出了一个类型参数,则该接口将拥有一个值迭代器并提供指定类型的值。如果给出了两个类型参数,则该接口将拥有一个对迭代器并提供具有给定类型的值对

给定一个键类型和一个值类型,一个值对是一个包含两个结构

  1. 一个,其名称为 "key", 被称为值对,其值为键类型的 IDL 值;

  2. 一个,其名称为 "value", 被称为值对,其值为值类型的 IDL 值。

一个值迭代器只能在支持索引属性的接口上声明。值迭代器的值类型必须与索引属性获取器返回的类型相同。值迭代器被隐式定义为迭代对象的索引属性。

一个键值对迭代器不能在支持索引属性的接口上声明。

具有键值对迭代器接口的相关文本必须为每个接口实例定义一个列表,该列表是该实例的值对,即迭代的值对列表。

值迭代器生成的JavaScript forEach方法的回调调用类似于Array.prototype.forEach,而对迭代器的forEach方法的回调调用则类似于Map.prototype.forEach。

由于值迭代器目前仅允许在支持索引属性的接口上使用,因此使用类似于Array的forEach方法是合理的。可能会需要在(a)不支持索引属性的接口上,或者(b)带有一个回调类似于Set.prototype.forEach(其中键与值相同)的forEach方法上使用值迭代器。如果你正在创建需要这种forEach方法的API,请提交问题

注意:这是数组迭代器对象的工作方式。对于支持索引属性的接口,entrieskeysvalues%Symbol.iterator%返回的迭代器对象是真正的数组迭代器对象

带有可迭代声明的接口必须不能有名为entriesforEachkeysvalues属性常量常规操作,也不能从拥有这些名称的属性、常量或常规操作的继承接口继承这些名称。

考虑以下接口SessionManager,它允许通过用户名访问多个Session对象:

[Exposed=Window]
  interface SessionManager {
    Session getSessionForUser(DOMString username);
  
    iterable<DOMString, Session>;
  };
  
  [Exposed=Window]
  interface Session {
    readonly attribute DOMString username;
    // ...
  };
  

迭代器的行为可以这样定义:

要迭代的值对是由用户名为为与该用户名对应的SessionManager对象上的打开的Session对象组成的值对列表,按用户名排序。

在JavaScript语言绑定中,接口原型对象SessionManager 接口提供了一个values方法,该方法是一个函数,当调用时返回一个迭代器对象,该对象本身具有next方法,返回下一个要迭代的值。它还提供了keysentries方法,分别用于迭代会话对象的用户名和用户名/Session对象对。还提供了一个%Symbol.iterator%方法,允许SessionManagerfor..of循环中使用,该方法的返回值与entries方法相同:

// 获取一个SessionManager实例。
  // 假设它有两个用户的会话,"anna"和"brian"。
  var sm = getSessionManager();

  typeof SessionManager.prototype.values;            // 计算结果为 "function"
  var it = sm.values();                              // values() 返回一个迭代器对象
  String(it);                                        // 计算结果为 "[object SessionManager Iterator]"
  typeof it.next;                                    // 计算结果为 "function"

  // 该循环将首先记录"anna",然后是"brian"。
  for (;;) {
    let result = it.next();
    if (result.done) {
      break;
    }
    let session = result.value;
    console.log(session.username);
  }

  // 该循环也将记录"anna"和"brian"。
  for (let username of sm.keys()) {
    console.log(username);
  }

  // 另一种实现相同目标的方式。
  for (let [username, session] of sm) {
    console.log(username);
  }
  

一个接口不能有多个可迭代声明。一个具有可迭代声明的接口及其继承的接口不能有maplike声明setlike声明异步可迭代声明

以下扩展属性适用于可迭代声明: [CrossOriginIsolated]、 [Exposed] 和 [SecureContext]。

Iterable ::
      iterable < TypeWithExtendedAttributes OptionalType > ;
  
OptionalType ::
      , TypeWithExtendedAttributes
      ε
  

2.5.10. 异步可迭代声明

一个接口可以通过在接口的主体中使用异步可迭代声明(匹配AsyncIterable)来声明为异步可迭代。

interface interface_identifier {
    async iterable<value_type>;
    async iterable<value_type>(/* 参数... */);
    async iterable<key_type, value_type>;
    async iterable<key_type, value_type>(/* 参数... */);
  };
  

实现了声明为异步可迭代的接口的对象支持通过异步迭代来获取值序列。

如果只给出一个类型参数,则该接口具有值异步可迭代声明,并异步提供指定类型的值。如果给出了两个类型参数,则该接口具有键值对异步可迭代声明,并异步提供具有给定类型的键值对

如果给定,异步可迭代声明的参数 (匹配 ArgumentList)必须全部是可选参数

在 JavaScript 语言绑定中,一个异步可迭代的接口将在其接口原型对象上具有 %Symbol.asyncIterator%values 属性。如果该接口具有成对异步可迭代声明,它还将具有 entrieskeys 属性。所有这些方法都可以传递可选参数,这些参数对应于异步可迭代声明中的参数列表,并由异步迭代器初始化步骤(如果存在)处理。

考虑到这一点,所有参数都必须是可选的这一要求确保了在 JavaScript 绑定中,for-await-of 可以直接作用于接口的实例,因为 for-await-of 会在不带参数的情况下调用 %Symbol.asyncIterator% 方法。

与具有接口异步可迭代声明相关的描述性文字必须定义一个获取下一个迭代结果的算法。 该算法接收正在被迭代的接口的实例,以及异步迭代器本身(这有助于存储状态)。 它必须返回一个Promise,该Promise要么被拒绝,要么解决为一个特殊的迭代结束值,以表示迭代的结束,或者解决为以下之一:

对于值异步可迭代声明

一个声明中给定类型的值;

对于键值对异步可迭代声明

一个包含声明中第一个给定类型的值和第二个给定类型的值的元组

描述性文字还可以定义一个异步迭代器返回算法。该算法接收被迭代的接口的实例、异步迭代器本身,以及一个类型为any的单一参数值。在异步迭代器的提前终止情况下,调用该算法。该算法必须返回一个Promise;如果Promise兑现,兑现值将被忽略,但如果被拒绝,该失败将传递给异步迭代器API的使用者。

在JavaScript绑定中,该算法允许自定义当异步迭代器的return()方法被调用时的行为。这通常发生在breakreturn语句导致退出for-await-of循环时。

我们可以为throw()添加一个类似的钩子。目前还没有这样的需求,但如果您在创建需要此类功能的API,请提交一个问题

描述性文字还可以定义异步迭代器初始化步骤。这些步骤接收被迭代的接口的实例、新创建的迭代器对象,以及一个包含传递的参数(如果有的话)的IDL值列表

具有 异步可迭代声明的接口不应具有任何名为entrieskeysvalues属性常量,或常规操作,也不能具有任何继承了这些名称的继承接口

考虑以下接口SessionManager,它允许按用户名访问多个Session对象:

[Exposed=Window]
  interface SessionManager {
    Session getSessionForUser(DOMString username);
  
    async iterable<DOMString, Session>;
  };
  
  [Exposed=Window]
  interface Session {
    readonly attribute DOMString username;
    // ...
  };
  

迭代器的行为可以如下定义:

SessionManager异步迭代器iterator异步迭代器初始化步骤为:

  1. iterator当前状态设置为“尚未开始”。

要为SessionManagermanager及其异步迭代器iterator获取下一个迭代结果

  1. 创建一个新的promise

  2. 获取key的值(如果存在),否则为null:

    如果iterator当前状态是“尚未开始”

    manager的打开会话中按字典顺序排列的最小用户名

    否则

    manager的打开会话中按字典顺序排列且大于iterator当前状态的最小用户名

    注意:iterator当前状态可能已不在打开的会话中。

  3. 如果key为null,则:

    1. 使用迭代结束解决promise

  4. 否则:

    1. 获取与key对应的Session对象session

    2. 使用(username, session)解决promise

    3. iterator当前状态设置为username

  5. 返回promise

在JavaScript语言绑定中,接口原型对象中的SessionManager 接口具有一个values方法,当调用时,它返回一个异步迭代器对象,该对象具有next方法,该方法返回下一个迭代的值。它还有keysentries方法,分别迭代会话对象的用户名和(用户名,Session)对象对。它还具有一个%Symbol.asyncIterator%方法,允许在for await..of循环中使用SessionManager,该循环返回与entries方法相同的值:

// 获取SessionManager的实例。
  // 假设它有两个用户的会话:"anna"和"brian"。
  var sm = getSessionManager();
  
  typeof SessionManager.prototype.values;            // 计算结果为 "function"
  var it = sm.values();                              // values() 返回一个迭代器对象
  typeof it.next;                                    // 计算结果为 "function"
  
  // 此循环将打印 "anna" 和 "brian"。
  for await (let username of sm.keys()) {
    console.log(username);
  }
  
  // 另一种实现相同功能的方式。
  for await (let [username, session] of sm) {
    console.log(username);
  }
  

接口不能有多个异步可迭代声明。带有继承接口接口不能同时具有异步可迭代声明。具有异步可迭代声明接口及其继承接口不得具有类映射声明类集合声明可迭代声明

以下扩展属性适用于异步可迭代声明:[CrossOriginIsolated]、[Exposed]和[SecureContext]。

这些扩展属性目前尚未生效。当它们生效时,其效果将符合预期。

AsyncIterable ::
      async iterable < TypeWithExtendedAttributes OptionalType > OptionalArgumentList ;
  
OptionalArgumentList ::
      ( ArgumentList )
      ε
  

2.5.11. Maplike 声明

一个 接口 可以通过在接口主体中使用 maplike 声明 (匹配 ReadWriteMaplikereadonly MaplikeRest)声明为 maplike 声明

interface interface_identifier {
    readonly maplike<key_type, value_type>;
    maplike<key_type, value_type>;
  };
  

实现了类映射声明的接口的对象表示有序映射的键值对,初始为空,称为该对象的映射条目。 键和值的类型在类映射声明的尖括号中给出。键必须是唯一的。

规范作者可以修改映射条目的内容,这将在JavaScript代码中自动反映对象的内容。

类映射接口支持适用于语言绑定的查询映射条目的API。如果未使用readonly关键字,则还支持修改映射条目的API。

注意:在JavaScript语言绑定中,用于操作映射条目的API类似于JavaScript Map对象的API。如果使用了readonly关键字,这包括entriesforEachgethaskeysvalues%Symbol.iterator%方法以及size属性获取器。对于可读写的类映射,还包括cleardeleteset方法。

类映射接口不能有名为entriesforEachgethaskeyssizevalues属性常量常规操作,也不能具有任何具有这些名称的继承接口

可读写的类映射接口不能有名为cleardeleteset属性常量,也不能有任何具有这些名称的继承接口

注意:可读写的类映射接口可以有名为cleardeleteset常规操作,这些操作将覆盖这些方法的默认实现(定义在§ 3.7.11 类映射声明中)。如果定义了这样的常规操作,它们必须匹配各方法的输入和输出期望,定义在各自的默认实现部分中。

接口不能有多个类映射声明。 类映射接口的继承接口不能也有类映射声明。 类映射接口及其继承接口不得具有可迭代声明异步可迭代声明类集合声明索引属性获取器

ReadOnlyMember ::
      readonly ReadOnlyMemberRest
  
ReadOnlyMemberRest ::
      AttributeRest
      MaplikeRest
      SetlikeRest
  
ReadWriteMaplike ::
      MaplikeRest
  
MaplikeRest ::
      maplike < TypeWithExtendedAttributes , TypeWithExtendedAttributes > ;
  

本规范中未定义任何适用于类映射声明扩展属性

添加示例。

2.5.12. Setlike 声明

一个 接口 可以通过在接口主体中使用 setlike 声明 (匹配 ReadWriteSetlikereadonly SetlikeRest)声明为 setlike 声明

interface interface_identifier {
    readonly setlike<type>;
    setlike<type>;
  };
  

实现了类集合声明的接口的对象表示有序集合的值,初始为空,称为该对象的集合条目。 类集合声明的尖括号中给出了值的类型。值必须是唯一的。

规范作者可以修改集合条目的内容,这将在JavaScript代码中自动反映对象的内容。

类集合接口支持适用于语言绑定的查询集合条目的API。如果未使用readonly关键字,则还支持修改集合条目的API。

注意:在JavaScript语言绑定中,用于操作集合条目的API类似于JavaScript Set对象的API。如果使用了readonly关键字,这包括entriesforEachhaskeysvalues%Symbol.iterator%方法以及size属性获取器。对于可读写的类集合,还包括addcleardelete方法。

类集合接口不能有名为entriesforEachhaskeyssizevalues属性常量常规操作,也不能具有任何具有这些名称的继承接口

可读写的类集合接口不能有名为addcleardelete属性常量,也不能有任何具有这些名称的继承接口

注意:可读写的类集合接口可以有名为addcleardelete常规操作,这些操作将覆盖这些方法的默认实现(定义在§ 3.7.12 类集合声明中)。如果定义了这样的常规操作,它们必须匹配各方法的输入和输出期望,定义在各自的默认实现部分中。

接口不能有多个类集合声明。 类集合接口的继承接口不能也有类集合声明。 类集合接口及其继承接口不得具有可迭代声明异步可迭代声明类映射声明索引属性获取器

ReadOnlyMember ::
      readonly ReadOnlyMemberRest
  
ReadOnlyMemberRest ::
      AttributeRest
      MaplikeRest
      SetlikeRest
  
ReadWriteSetlike ::
      SetlikeRest
  
SetlikeRest ::
      setlike < TypeWithExtendedAttributes > ;
  

本规范中未定义任何适用于类集合声明扩展属性

添加示例。

2.6. 命名空间

命名空间是一个声明全局单例及其相关行为的定义(匹配Namespace)。

namespace identifier {
    /* namespace_members... */
  };
  

命名空间是常规操作只读 常规属性常量的集合规范(匹配NamespaceMembers),这些操作和属性描述了打包到命名空间中的行为。

与接口一样,命名空间的IDL可以通过使用部分命名空间定义(匹配partial Namespace)拆分为多个部分。部分命名空间定义的标识符必须与命名空间定义的标识符相同。每个部分命名空间定义中出现的成员都被视为命名空间本身的成员。

namespace SomeNamespace {
    /* namespace_members... */
  };
  
  partial namespace SomeNamespace {
    /* namespace_members... */
  };
  

注意:与部分接口定义一样,部分命名空间定义旨在作为规范编辑的辅助工具,允许命名空间定义分散在文档的多个部分,有时甚至是多个文档。

成员出现的顺序对于JavaScript绑定中的属性枚举具有重要意义。

注意,与接口或字典不同,命名空间不会创建类型。

在本规范中定义的扩展属性中,只有[CrossOriginIsolated]、[Exposed]和[SecureContext]扩展属性适用于命名空间。

命名空间必须带有[Exposed] 扩展属性

Partial ::
      partial PartialDefinition
  
PartialDefinition ::
      interface PartialInterfaceOrPartialMixin
      PartialDictionary
      Namespace
  
Namespace ::
      namespace identifier { NamespaceMembers } ;
  
NamespaceMembers ::
      ExtendedAttributeList NamespaceMember NamespaceMembers
      ε
  
NamespaceMember ::
      RegularOperation
      readonly AttributeRest
      Const
  

以下IDL片段定义了一个命名空间

namespace VectorUtils {
    readonly attribute Vector unit;
    double dotProduct(Vector x, Vector y);
    Vector crossProduct(Vector x, Vector y);
  };
  

JavaScript实现将公开一个全局VectorUtils数据属性,它是一个简单的对象(原型为%Object.prototype%),其中每个声明的操作都是可枚举的数据属性,每个声明的属性都是可枚举的只读访问器:

Object.getPrototypeOf(VectorUtils);                         // 计算结果为Object.prototype。
  Object.keys(VectorUtils);                                   // 计算结果为["dotProduct", "crossProduct"]。
  Object.getOwnPropertyDescriptor(VectorUtils, "dotProduct"); // 计算结果为{ value: <a function>, enumerable: true, configurable: true, writable: true }。
  Object.getOwnPropertyDescriptor(VectorUtils, "unit");       // 计算结果为{ get: <a function>, enumerable: true, configurable: true }。
  

2.7. 字典

字典是一种定义(匹配Dictionary),用于定义具有固定、有序有序映射条目集合的数据类型,这些条目称为字典成员,其中是字符串,具有在定义中指定的特定类型。

dictionary identifier {
    /* dictionary_members... */
  };
  

字典实例不会保留对其语言特定表示(例如,对应的JavaScript对象)的引用。因此,例如,从操作返回字典将从字典的当前值创建一个新的JavaScript对象,并且,接受字典作为参数的操作将基于当前的JavaScript对象属性进行一次性转换。对字典的修改不会反映在对应的JavaScript对象中,反之亦然。

字典不能用作属性常量的类型。

字典可以定义为从另一个字典继承。如果字典的标识符后面跟着冒号和标识符,则该标识符标识继承的字典。标识符必须标识一个字典。

字典不能声明为使其继承层次结构产生循环。也就是说,字典A不能继承自自身,也不能继承自另一个继承自A的字典B,等等。

dictionary Base {
    /* dictionary_members... */
  };
  
  dictionary Derived : Base {
    /* dictionary_members... */
  };
  

给定字典D继承字典是所有直接或间接从D继承的字典的集合。如果D没有从另一个字典继承,则该集合为空。否则,集合包括D继承自的字典E及其所有的继承字典

字典成员可以指定为必需,这意味着将语言特定的值转换为字典时,必须为该成员提供值。任何未被指定为必需的字典成员都是可选的。

请注意,将字典成员指定为必需仅在将其他表示形式的字典(如作为操作参数提供的JavaScript值)转换为IDL字典时具有可观察的效果。在所有其他情况下,包括字典类型仅作为操作返回类型时,规范作者应将成员保留为可选

类型为D的字典值可以具有D定义的每个字典成员和D的任何继承字典中的字典成员的条目。指定为必需的字典成员,或指定为具有默认值的字典成员,将始终具有相应的条目。其他成员的条目可能存在,也可能不存在于字典值中。

在JavaScript绑定中,属性对应的字典成员的值为undefined时,等同于省略该属性。因此,如果该成员是必需的,将会导致错误;如果存在默认值,将触发该默认值;否则,字典值中将不会存在该条目

操作参数默认值一样,强烈建议不要使用true作为boolean类型字典成员默认值,因为这可能会让作者感到困惑,他们可能期望使用默认的undefined转换(即false)。[API-DESIGN-PRINCIPLES]

具有字符串键的有序映射可以隐式视为特定字典D的字典值,如果其所有条目都对应于字典成员,并且这些条目具有正确的类型,并且存在针对任何必需默认的字典成员的条目。

dictionary Descriptor {
    DOMString name;
    sequence<unsigned long> serviceIdentifiers;
  };
  

可以按照以下步骤创建Descriptor字典:

  1. identifiers为« 1, 3, 7 »。

  2. 返回«[ "name" → "test", "serviceIdentifiers" → identifiers ]»。

每个字典成员(匹配DictionaryMember)被指定为一个类型(匹配Type),后跟一个标识符(由类型后面的identifier标记给出)。标识符是键值对的键名。如果Type是一个后跟?标识符,那么该标识符必须标识一个接口枚举回调函数回调接口类型定义。如果字典成员类型是一个未后跟?的标识符,则该标识符必须标识这些定义之一或一个字典

如果字典成员的类型在解析类型定义后是可空类型,则其内部类型不得是字典类型

dictionary identifier {
    type identifier;
  };
  

如果可选字典成员的标识符后面跟着U+003D (=)和一个值(匹配DefaultValue),则该值为字典成员的默认值,当作者代码或规范文本未为该成员提供值时,默认使用此值。

dictionary identifier {
    type identifier = "value";
  };
  

当布尔文字标记(truefalse)、null标记、整数标记、小数标记、三个特殊的浮点文字值之一(Infinity-InfinityNaN)、字符串标记、[]标记或{}标记作为默认值使用时,它的解释与操作可选参数默认值相同。

如果字典成员的类型是枚举,那么它的默认值(如果指定)必须是该枚举值之一。

如果字典成员的类型前有required关键字,则该成员被视为必需的字典成员

dictionary identifier {
    required type identifier;
  };
  

字典成员的类型不能包含它所在的字典。如果类型包含字典D,则至少满足以下条件之一:

与接口类似,字典的IDL可以通过使用部分字典定义(匹配partialDictionary)拆分为多个部分。

dictionary SomeDictionary {
    /* dictionary_members... */
  };
  
  partial dictionary SomeDictionary {
    /* dictionary_members... */
  };
  

注意:与部分接口定义类似,部分字典定义用于作为规范编辑的辅助工具,允许将字典的定义分散在文档的多个部分,有时甚至是多个文档中。

在给定字典中的字典成员的顺序是继承的字典成员排在非继承成员之前,并且字典定义(包括任何部分字典定义)中的字典成员按组成其标识符的Unicode代码点的字典顺序排列。

例如,以下定义:

dictionary B : A {
    long b;
    long a;
  };
  
  dictionary A {
    long c;
    long g;
  };
  
  dictionary C : B {
    long e;
    long f;
  };
  
  partial dictionary A {
    long h;
    long d;
  };
  

类型C的字典值的字典成员的顺序是c、d、g、h、a、b、e、f。

字典需要有其成员的顺序,因为在某些语言绑定中,传递字典值给平台对象时观察到的行为取决于提取字典成员的顺序。例如,考虑以下附加接口:

[Exposed=Window]
  interface Something {
    undefined f(A a);
  };
  

以及以下JavaScript代码:

var something = getSomething();  // Get an instance of Something.
  var x = 0;
  
  var dict = { };
  Object.defineProperty(dict, "d", { get: function() { return ++x; } });
  Object.defineProperty(dict, "c", { get: function() { return ++x; } });
  
  something.f(dict);
  

提取字典成员的顺序决定了它们将被认为具有的值。由于A的顺序定义为先c再d,所以c的值将为1,而d的值将为2。

字典成员的标识符不能与字典中定义的另一个字典成员或该字典的继承字典中的字典成员相同。

没有扩展属性适用于字典。

Partial ::
      partial PartialDefinition
  
PartialDefinition ::
      interface PartialInterfaceOrPartialMixin
      PartialDictionary
      Namespace
  
Dictionary ::
      dictionary identifier Inheritance { DictionaryMembers } ;
  
DictionaryMembers ::
      DictionaryMember DictionaryMembers
      ε
  
DictionaryMember ::
      ExtendedAttributeList DictionaryMemberRest
  
DictionaryMemberRest ::
      required TypeWithExtendedAttributes identifier ;
      Type identifier Default ;
  
PartialDictionary ::
      dictionary identifier { DictionaryMembers } ;
  
Default ::
      = DefaultValue
      ε
  
DefaultValue ::
      ConstValue
      string
      [ ]
      { }
      null
      undefined
  
Inheritance ::
      : identifier
      ε
  

字典类型的一个用途是允许操作拥有多个可选参数,而不必在调用时受限于指定它们的顺序。例如,考虑以下IDL片段

[Exposed=Window]
interface Point {
  constructor();
  attribute double x;
  attribute double y;
};

dictionary PaintOptions {
  DOMString fillPattern = "black";
  DOMString strokePattern;
  Point position;
};

[Exposed=Window]
interface GraphicsContext {
  undefined drawRectangle(double width, double height, optional PaintOptions options);
};

在IDL的JavaScript实现中,可以为可选PaintOptions字典传入一个对象:

// 获取GraphicsContext的实例。
var ctx = getGraphicsContext();

// 画一个矩形。
ctx.drawRectangle(300, 200, { fillPattern: "red", position: new Point(10, 10) });

PaintOptions的成员是可选的。如果省略了fillPatterndrawRectangle的定义可以假定它具有给定的默认值,而无需包含显式处理其省略的措辞。drawRectangle需要显式处理省略strokePatternposition的情况。

2.8. 异常

异常是一种表示错误的对象类型,能够被抛出或作为一等值处理。Web IDL 预定义了一些异常类型,这些异常类型可以在规范定义的操作、属性等中引用并抛出。也可以自定义异常类型,作为继承自DOMException接口来定义。

简单异常可以通过以下类型之一标识:

这些对应于所有 JavaScript 错误对象 (除了 SyntaxErrorError,它们被故意省略, 因为它们分别被保留给 JavaScript 解析器和作者使用)。每个 简单异常 的含义与其在 JavaScript 规范中的相应错误对象匹配。

第二种类型的异常是 DOMException, 它通过提供一个 名称 来提供有关发生错误的进一步编程可检查的详细信息。 此类 名称 来自下方的 DOMException 名称表

由于 DOMException 是一个 接口类型, 它可以在 IDL 中用作类型。这允许,例如,可以声明一个 操作 具有 DOMException返回类型。然而,这通常是一个不好的模式,因为异常应当被抛出而不是返回。

最后一种类型的异常是 DOMException 的派生接口。 它们更复杂,因此在专门章节 § 2.8.2 DOMException 派生接口 中进行了描述。

简单异常 可以通过提供其类型名称进行 创建。 可以通过提供其 名称 后接 DOMException 来创建一个 DOMException。 异常也可以通过提供创建所需的相同详细信息进行 抛出。 在这两种情况下,调用方可以提供关于异常指示内容的额外信息,这在构造异常的消息时非常有用。

以下是一些用于创建和抛出异常的措辞示例。要抛出一个新类型为TypeError简单异常

抛出一个TypeError

要抛出一个带有名称NotAllowedError的新DOMException

抛出一个"NotAllowedError"DOMException

要创建一个带有名称SyntaxError的新DOMException

对象成为一个新SyntaxErrorDOMException

要用名称OperationError的新DOMException拒绝一个Promise:

拒绝p,并使用一个"OperationError"DOMException

以下是包含构造异常消息时附加信息的示例:

抛出一个"SyntaxError"DOMException,表明给定的值包含不允许的尾随空格。

当异常抛出的原因不立即明显时,这类附加上下文对实现者非常有帮助,例如,当算法中的许多不同步骤都会抛出一个"SyntaxError"DOMException。相反,如果你的规范在检查用户是否提供了使用某个功能的权限之后立即抛出一个"NotAllowedError"DOMException,那么构造的消息应该很明显,因此无需明确说明。

创建和抛出异常的结果行为取决于语言绑定。

有关在JavaScript语言绑定中创建和抛出异常的详细信息,请参阅§ 3.14.3 创建和抛出异常

2.8.1. 基础DOMException 错误名称

下面的DOMException名称表列出了基础DOMException 接口实例的所有允许的名称,以及这些名称的含义和遗留的数字错误代码值。

继承自DOMException接口(如§ 2.8.2 DOMException派生接口中所述),将拥有自己的名称,未列入此表。

创建抛出一个DOMException时,规范必须使用这些名称之一。如果规范作者认为这些名称都不适合其案例,他们必须提交问题,以讨论将新名称添加到共享命名空间,方便社区协调这些工作。请注意,只有当您认为Web开发人员会区分同一API产生的多个错误情况时,添加新的特定用途的名称才显得重要。

DOMException名称标记为过时是为了兼容遗留问题,但不推荐使用这些名称。

注意:不要混淆这里定义的"SyntaxError"DOMException与JavaScript中的SyntaxError。"SyntaxError"DOMException用于报告Web API中的解析错误,例如解析选择器时,而JavaScript的SyntaxError是为JavaScript解析器保留的。为了进一步避免混淆,请始终优先使用"SyntaxError"DOMException符号,而不是仅使用SyntaxError来指代DOMException[DOM]

名称 描述 遗留代码名称和值
"IndexSizeError" 已废弃。 请使用RangeError代替。 INDEX_SIZE_ERR (1)
"HierarchyRequestError" 该操作将导致一个错误的节点树[DOM] HIERARCHY_REQUEST_ERR (3)
"WrongDocumentError" 该对象位于错误的文档中。[DOM] WRONG_DOCUMENT_ERR (4)
"InvalidCharacterError" 字符串包含无效字符。 INVALID_CHARACTER_ERR (5)
"NoModificationAllowedError" 该对象无法被修改。 NO_MODIFICATION_ALLOWED_ERR (7)
"NotFoundError" 对象在此处找不到。 NOT_FOUND_ERR (8)
"NotSupportedError" 该操作不被支持。 NOT_SUPPORTED_ERR (9)
"InUseAttributeError" 该属性正在被另一个元素使用。[DOM] INUSE_ATTRIBUTE_ERR (10)
"InvalidStateError" 对象处于无效状态。 INVALID_STATE_ERR (11)
"SyntaxError" 字符串不符合预期的模式。 SYNTAX_ERR (12)
"InvalidModificationError" 对象无法以这种方式修改。 INVALID_MODIFICATION_ERR (13)
"NamespaceError" 该操作不被XML命名空间允许。[XML-NAMES] NAMESPACE_ERR (14)
"InvalidAccessError" 已废弃。 对于无效参数,请使用TypeError;对于不支持的操作,请使用"NotSupportedError"DOMException;对于被拒绝的请求,请使用"NotAllowedError"DOMException INVALID_ACCESS_ERR (15)
"TypeMismatchError" 已废弃。 请使用TypeError代替。 TYPE_MISMATCH_ERR (17)
"SecurityError" 操作不安全。 SECURITY_ERR (18)
"NetworkError" 发生了网络错误。 NETWORK_ERR (19)
"AbortError" 操作已中止。 ABORT_ERR (20)
"URLMismatchError" 已废弃。 URL_MISMATCH_ERR (21)
"QuotaExceededError" 配额已超出。 QUOTA_EXCEEDED_ERR (22)
"TimeoutError" 操作超时。 TIMEOUT_ERR (23)
"InvalidNodeTypeError" 提供的节点不正确或具有错误的祖先节点。[DOM] INVALID_NODE_TYPE_ERR (24)
"DataCloneError" 对象无法克隆。 DATA_CLONE_ERR (25)
"EncodingError" 编码操作(编码或解码)失败。
"NotReadableError" I/O 读取操作失败。
"UnknownError" 由于未知的临时原因(例如内存不足),操作失败。
"ConstraintError" 事务中的变更操作因未满足约束条件而失败。[INDEXEDDB]
"DataError" 提供的数据不足。
"TransactionInactiveError" 对当前未激活或已完成的事务发出了请求。[INDEXEDDB]
"ReadOnlyError" 在“只读”事务中尝试执行变更操作。[INDEXEDDB]
"VersionError" 尝试以低于现有版本的方式打开数据库。[INDEXEDDB]
"OperationError" 操作因特定原因失败。
"NotAllowedError" 当前上下文中用户代理或平台不允许请求,可能是因为用户拒绝了权限。
"OptOutError" 用户选择退出了该过程。

2.8.2. DOMException 派生接口

当异常需要携带可供程序检测的额外信息,而这些信息超出了 DOMException名称范围时,规范作者可以创建一个接口,该接口继承DOMException。这些接口必须遵循特定的规则,以确保开发者可以预测其行为。具体规则如下:

这些要求意味着这些接口的继承 代码 属性将始终返回 0。

继承自 DOMException 的派生接口,携带了额外的“协议错误代码”,该错误代码来自于服务器通过某个假设的网络协议“协议 X”发送的数据,定义如下:

[Exposed=Window, Serializable]
  interface ProtocolXError : DOMException {
    constructor(optional DOMString message = "", ProtocolXErrorOptions options);
  
    readonly attribute unsigned long long errorCode;
  };
  
  dictionary ProtocolXErrorOptions {
      required [EnforceRange] unsigned long long errorCode;
  };
  

每个 ProtocolXError 实例都有一个错误代码,是一个数字。

构造函数 new ProtocolXError(message, options) 的步骤如下:

  1. this名称 设置为 "ProtocolXError"。
  2. this消息 设置为 message
  3. this错误代码 设置为 options["errorCode"]。

errorCode 的 getter 步骤是返回 this错误代码

ProtocolXError 对象是 可序列化对象

它们的序列化步骤如下,给定 valueserialized

  1. 根据 DOMException 序列化步骤,传递 valueserialized
  2. serialized 的 [[ErrorCode]] 设置为 value错误代码

它们的反序列化步骤如下,给定 serializedvalue

  1. 根据 DOMException 反序列化步骤,传递 serializedvalue
  2. value错误代码 设置为 serialized 的 [[ErrorCode]]。

创建抛出一个 DOMException 派生接口,提供其 接口 标识符以及构造它所需的额外信息。

要抛出 ProtocolXError 的一个实例,如上示例所示:

抛出一个 ProtocolXError,其错误代码为 42。

2.9. 枚举

枚举是一种用于声明类型的定义(匹配Enum),其有效值为一组预定义的字符串。枚举可以用来限制DOMString值,该值可以分配给属性或传递给操作

enum identifier { "enum", "values" /* , ... */ };

枚举值通过以逗号分隔的string字面量列表指定。枚举值列表不得包含重复项。

强烈建议枚举值全为小写,并且多个单词使用连字符分隔或不分隔,除非有特定原因使用其他命名方案。例如,指示应创建对象的枚举值可以命名为 "createobject" 或 "create-object"。在决定是否连字符分隔或不分隔枚举值单词时,请考虑相关 API 的一致性。

当字符串值不是有效的枚举值并赋值给属性或传递给枚举类型的操作参数时,行为是语言绑定特定的。

注意:在 JavaScript 绑定中,将无效的字符串值赋给属性时会被忽略,而在其他上下文(例如作为操作参数)中传递此类值时会抛出异常。

此规范中定义的没有适用于枚举的扩展属性

Enum ::
      enum identifier { EnumValueList } ;
  
EnumValueList ::
      string EnumValueListComma
  
EnumValueListComma ::
      , EnumValueListString
      ε
  
EnumValueListString ::
      string EnumValueListComma
      ε
  

以下 IDL 片段定义了一个枚举,该枚举用作属性操作参数的类型:

enum MealType { "rice", "noodles", "other" };
  
  [Exposed=Window]
  interface Meal {
      attribute MealType type;
      attribute double size;     // 以克为单位
  
    undefined initialize(MealType type, double size);
  };
  

JavaScript 实现会限制可以分配给 type 属性或传递给 initializeMeal 函数的字符串,这些字符串必须是 枚举 中定义的字符串。

var meal = getMeal();                // 获取一个 Meal 实例。
  
  meal.initialize("rice", 200);        // 正常调用操作。
  
  try {
    meal.initialize("sandwich", 100);  // 抛出 TypeError。
  } catch (e) {
  }

  meal.type = "noodles";               // 属性正常分配。
  meal.type = "dumplings";             // 属性分配被忽略。
  meal.type == "noodles";              // 计算结果为 true。

2.10. 回调函数

“Custom DOM Elements” 规范希望使用回调函数类型来提供平台对象函数。我们是否应该将“回调函数”重命名为仅“函数”,以明确它们可以用于两种目的?

回调函数 是一种定义(匹配 callback CallbackRest),用于声明函数类型。

callback identifier = return_type (/* 参数... */);

注意:另请参见类似命名的 回调接口

等号左边的 标识符 给出了 回调函数 的名称,等号右边的返回类型和参数列表(匹配 TypeArgumentList)则给出了回调函数类型的签名。

回调函数 不能用作 常量 的类型。

以下扩展属性适用于回调函数: [LegacyTreatNonObjectAsNull]。

CallbackOrInterfaceOrMixin ::
      callback CallbackRestOrInterface
      interface InterfaceOrMixin
  
CallbackRest ::
      identifier = Type ( ArgumentList ) ;
  

以下 IDL 片段 定义了一个用于 API 的 回调函数,该 API 会在操作完成时调用用户定义的函数。

callback AsyncOperationCallback = undefined (DOMString status);
  
  [Exposed=Window]
  interface AsyncOperations {
    undefined performOperation(AsyncOperationCallback whenFinished);
  };
  

在 JavaScript 语言绑定中,函数对象 作为操作参数传递。

var ops = getAsyncOperations();  // 获取 AsyncOperations 的实例。
  
  ops.performOperation(function(status) {
    window.alert("操作完成,状态为 " + status + ".");
  });
  

2.11. 类型定义

类型定义 是一种定义 (匹配 Typedef) 用于声明一个类型的新名称。这个新名称不会通过语言绑定暴露出来;它纯粹作为在 IDL 中引用该类型的简写使用。

typedef type identifier;

被赋予新名称的类型typedef 关键字之后指定(匹配 TypeWithExtendedAttributes),后面的 标识符 token 提供了新名称。

类型 不得是相同或其他 类型定义 的标识符。

本规范中没有任何定义的 扩展属性 适用于 类型定义

Typedef ::
      typedef TypeWithExtendedAttributes identifier ;
  

以下 IDL 片段 展示了使用 类型定义 来允许使用简短的 标识符 代替较长的 序列类型

[Exposed=Window]
  interface Point {
    attribute double x;
    attribute double y;
  };
  
  typedef sequence<Point> Points;
  
  [Exposed=Window]
  interface Widget {
    boolean pointWithinBounds(Point p);
    boolean allPointsWithinBounds(Points ps);
  };
  

2.12. 实现接口的对象

在一组 IDL 片段 的给定实现中, 一个对象可以被描述为 平台对象

平台对象 是实现了 接口 的对象。

遗留平台对象平台对象,它实现了没有 [Global] 扩展属性 的接口,并且支持 索引属性命名属性,或者两者兼有。

例如,在浏览器中, 浏览器实现的 DOM 对象(实现了如 NodeDocument 这样的接口)为页面中运行的 JavaScript 提供对网页内容的访问权限,这些对象将被视为 平台对象。这些对象可能是异质对象,使用 C++ 语言实现,或者它们可能是原生的 JavaScript 对象。不管怎样, 一个给定的 IDL 片段集合的实现需要能够识别所有由该实现创建的 平台对象。这可以通过某种内部状态记录一个给定对象是否确实是该实现的一个平台对象,或者可能通过观察 该对象是由某个给定的内部 C++ 类实现的。具体来说,平台对象如何由一组 IDL 片段的给定实现识别是实现相关的。

系统中的所有其他对象不会被视为平台对象。例如,假设 一个在浏览器中打开的网页加载了一个 JavaScript 库,该库实现了 DOM Core。这个库 将被视为与浏览器提供的实现不同的实现。 由该 JavaScript 库创建的实现 Node 接口的对象 不会被浏览器实现视为实现 Node 的平台对象。

另一方面,回调接口 可以由任何 JavaScript 对象实现。这 允许 Web API 调用作者定义的操作。例如,DOM 事件的实现 允许作者通过提供实现了 EventListener 接口的对象来注册回调。

2.13. 类型

本节列出了 Web IDL 支持的类型、 每个类型对应的值集合或 Infra 类型, 以及该类型的常量的表示方式。

以下类型被称为整数类型byteoctetshortunsigned shortlongunsigned longlong longunsigned long long

以下类型被称为数值类型整数类型floatunrestricted floatdoubleunrestricted double

原始类型 包括 bigintboolean, 以及数值类型

字符串类型 包括 DOMString、 所有枚举类型ByteStringUSVString

缓冲区类型 包括 ArrayBufferSharedArrayBuffer

类型化数组类型 包括 Int8ArrayInt16ArrayInt32ArrayUint8ArrayUint16ArrayUint32ArrayUint8ClampedArrayBigInt64ArrayBigUint64ArrayFloat16ArrayFloat32Array、 和 Float64Array

缓冲区视图类型 包括 DataView类型化数组类型

缓冲区源类型 包括 缓冲区类型缓冲区视图类型

object 类型、 所有 接口类型 和 所有 回调接口类型 被称为对象类型

当从语言绑定的特定类型到 IDL 类型进行转换以调用操作或为属性分配值时, 在执行操作或属性分配的指定功能之前,将执行所有必要的转换。如果无法执行转换,则操作不会运行或 属性不会更新。在某些语言绑定中, 类型转换可能会导致抛出异常。 在这种情况下,这些异常将传播到尝试调用操作或 为属性赋值的代码中。

Type ::
      SingleType
      UnionType Null
  
TypeWithExtendedAttributes ::
      ExtendedAttributeList Type
  
SingleType ::
      DistinguishableType
      any
      PromiseType
  
UnionType ::
      ( UnionMemberType or UnionMemberType UnionMemberTypes )
  
UnionMemberType ::
      ExtendedAttributeList DistinguishableType
      UnionType Null
  
UnionMemberTypes ::
      or UnionMemberType UnionMemberTypes
      ε
  
DistinguishableType ::
    PrimitiveType Null
    StringType Null
    identifier Null
    sequence < TypeWithExtendedAttributes > Null
    async_iterable < TypeWithExtendedAttributes > Null
    object Null
    symbol Null
    BufferRelatedType Null
    FrozenArray < TypeWithExtendedAttributes > Null
    ObservableArray < TypeWithExtendedAttributes > Null
    RecordType Null
    undefined Null
ConstType ::
      PrimitiveType
      identifier
  
PrimitiveType ::
      UnsignedIntegerType
      UnrestrictedFloatType
      boolean
      byte
      octet
      bigint
  
UnrestrictedFloatType ::
      unrestricted FloatType
      FloatType
  
FloatType ::
      float
      double
  
UnsignedIntegerType ::
      unsigned IntegerType
      IntegerType
  
IntegerType ::
      short
      long OptionalLong
  
OptionalLong ::
      long
      ε
  
StringType ::
      ByteString
      DOMString
      USVString
  
PromiseType ::
      Promise < Type >
  
RecordType ::
      record < StringType , TypeWithExtendedAttributes >
  
Null ::
      ?
      ε
  

2.13.1. 任意类型

any 类型是所有其他可能的非联合类型的并集。

any 类型类似于区分联合类型,因为它的每个值都有一个特定的非any类型与之关联。例如,any 类型的一个值是 unsigned long 150,而另一个是 long 150。这些是不同的值。

any 值的特定类型称为其特定类型。(联合类型的值也有特定类型。)

2.13.2. undefined 类型

undefined 类型有一个唯一的值。

undefined 常量值在 IDL 中用 undefined token 表示。

undefined 不能在任何情况下作为参数类型(在 操作回调函数构造函数操作等中),也不能作为 字典成员的类型,无论是直接使用还是在联合类型中使用。相反,应该使用可选参数或非必需字典成员

注意: 此值以前拼写为 void,并且在使用上受到更多限制。

2.13.3. 布尔类型

boolean 类型对应于布尔值

IDL 中的 boolean 常量值用 truefalse 标记表示。

2.13.4. 字节类型

byte 类型对应于8 位有符号整数

IDL 中的 byte 常量值用 整数 标记表示。

2.13.5. 八位字节类型

octet 类型对应于8 位无符号整数

IDL 中的 octet 常量值用 整数 标记表示。

2.13.6. 短整型

short 类型对应于16 位有符号整数

IDL 中的 short 常量值用 整数 标记表示。

2.13.7. 无符号短整型

unsigned short 类型对应于16 位无符号整数

IDL 中的 unsigned short 常量值用 整数 标记表示。

2.13.8. 长整型

long 类型对应于32 位有符号整数

IDL 中的 long 常量值用 整数 标记表示。

2.13.9. 无符号长整型

unsigned long 类型对应于32 位无符号整数

IDL 中的 unsigned long 常量值用 整数 标记表示。

2.13.10. 长长整型

long long 类型对应于64 位有符号整数

IDL 中的 long long 常量值用 整数 标记表示。

2.13.11. 无符号长长整型

unsigned long long 类型对应于64 位无符号整数

IDL 中的 unsigned long long 常量值用 整数 标记表示。

2.13.12. 单精度浮点型

float 类型是一种浮点数值类型,对应于有限单精度 32 位 IEEE 754 浮点数的集合。[IEEE-754]

IDL 中的 float 常量值用 十进制 标记表示。

除非有特定原因需要使用 32 位浮点类型,否则规范应使用 double 而不是 float,因为 double 可以表示的值集更接近 JavaScript Number。

2.13.13. 无限制单精度浮点型

unrestricted float 类型是一种浮点数值类型,对应于所有可能的单精度 32 位 IEEE 754 浮点数的集合,包括有限值、非有限值和特殊的“非数字”值 (NaN)。[IEEE-754]

IDL 中的 unrestricted float 常量值用 十进制 标记表示。

2.13.14. 双精度浮点型

double 类型是一种浮点数值类型,对应于有限双精度 64 位 IEEE 754 浮点数的集合。[IEEE-754]

IDL 中的 double 常量值用 十进制 标记表示。

2.13.15. 无限制双精度浮点型

unrestricted double 类型是一种浮点数值类型,对应于所有可能的双精度 64 位 IEEE 754 浮点数的集合,包括有限值、非有限值和特殊的“非数字”值 (NaN)。[IEEE-754]

IDL 中的 unrestricted double 常量值用 十进制 标记表示。

2.13.16. 大整数类型

bigint 类型是一种任意整数类型,范围不受限制。

IDL 中的 bigint 常量值用 整数 标记表示。

2.13.17. DOM字符串类型

DOMString 类型对应于字符串

注意: null 不是 DOMString 类型的值。要允许 null,需要使用可为空的 DOMString,在 IDL 中写作 DOMString?

注意: DOMString 值可能包含未配对的代理码点。如果不希望如此,请使用 USVString

IDL 中没有办法表示常量 DOMString 值,尽管 DOMString 字典成员默认值操作可选参数默认值可以设置为字符串字面量的值

2.13.18. 字节字符串类型

ByteString 类型对应于字节序列

在 IDL 中没有办法表示 ByteString 的常量值,但可以将 ByteString字典成员 默认值操作的可选参数 默认值 设置为 字符串字面量的值。

规范应仅在与同时使用字节和字符串的协议(如 HTTP)进行交互时使用 ByteString。一般来说,字符串应使用 DOMString 值表示,即使预期字符串的值总是 ASCII 或某些 8 位字符编码。应使用带有 octetbyte 元素的序列冻结数组,或者使用 Uint8ArrayInt8Array 来保存 8 位数据,而不是使用 ByteString

2.13.19. USV字符串类型

USVString 类型对应于标量值字符串。 根据上下文, 它们可以被视为代码单元标量值的序列。

在 IDL 中没有办法表示 USVString 的常量值,但可以将 USVString字典成员 默认值操作可选参数 默认值 设置为 字符串字面量的值。

规范应仅在执行文本处理并需要一串标量值的 API 中使用 USVString。大多数使用字符串的 API 应使用 DOMString,它不对字符串中的代码单元进行任何解释。如果不确定,请使用 DOMString

2.13.20. 对象类型

object 类型对应于所有可能的非空对象引用集合。

在 IDL 中没有办法表示 object 常量值。

若要表示包含所有可能对象引用和 null 值的类型,请使用可空类型 object?

2.13.21. symbol

symbol 类型对应于所有可能的符号值集合。符号值是不可见的非对象值,但具有标识(即,仅等于其自身)。

在 IDL 中没有办法表示 symbol 的常量值。

2.13.22. 接口类型

用于标识接口标识符,用来指代与所有可能的非空对象引用集合对应的类型,这些对象实现了该接口。

接口类型的 IDL 值仅由对象引用表示。

在 IDL 中没有办法表示特定接口类型的常量对象引用值。

若要表示包含所有可能引用该接口实现的对象及 null 值的类型,请使用可空类型

2.13.23. 回调接口类型

用于标识回调接口标识符,用来指代与所有可能的非空对象引用集合对应的类型。

回调接口类型的 IDL 值由对象引用和回调上下文元组表示。回调上下文是语言绑定的特定值,用于存储在语言绑定的特定对象引用被转换为 IDL 值时的执行上下文信息。

注意: 对于 JavaScript 对象,回调上下文用于保存将对象值转换为 IDL 回调接口类型值时的当前设置对象的引用。参见§ 3.2.16 回调接口类型

在 IDL 中没有办法表示特定回调接口类型的常量对象引用值。

若要表示包含所有可能对象引用及 null 值的类型,请使用可空类型

2.13.24. 字典类型

用于标识字典标识符,用来指代与符合该字典定义的所有字典集合对应的类型。

当从上下文中隐含理解为将有序映射视为特定字典类型的实例时,有序映射的字面语法也可以用于表示字典。然而,IDL 片段中没有办法表示常量字典值。

2.13.25. 枚举类型

用于标识枚举标识符,用来指代一个类型,该类型的值为字符串(代码单元序列,如DOMString)。这些字符串是枚举的值

DOMString 相似,IDL 中没有办法表示常量枚举值,但枚举类型的 字典成员 默认值操作的可选参数 默认值可以设置为 字符串字面量的值。

2.13.26. 回调函数类型

标识回调函数标识符,用来指代类型,其值是对具有指定签名的函数对象的引用。

注意: 如果在 定义中的 扩展属性指定了 [LegacyTreatNonObjectAsNull],则其值可以是对非函数对象的引用。

回调函数类型的 IDL 值由对象引用和回调上下文元组表示。

注意:回调接口类型类似,回调上下文用于保存将 JavaScript 对象值转换为 IDL 回调函数类型值时的当前设置对象的引用。参见§ 3.2.19 回调函数类型

在 IDL 中没有办法表示常量回调函数值。

2.13.27. 可空类型 — T?

可空类型是从现有类型(称为内部类型)构建的 IDL 类型,该类型允许其值集合中包含额外的 null 值。可空类型在 IDL 中通过在现有类型后添加 U+003F (?) 字符表示。内部类型不能是:

注意: 虽然字典类型通常是可空的,但作为操作参数或字典成员时,它们不能为可空类型。

可空类型的常量值在 IDL 中的表示方式与其内部类型的常量值表示方式相同,或使用 null 令牌。

例如,允许 truefalsenull 值的类型写作 boolean?

[Exposed=Window]
  interface NetworkFetcher {
    undefined get(optional boolean? areWeThereYet = false);
  };
  

以下接口有两个属性:其中一个的值可以是 DOMStringnull 值,另一个的值可以是 Node 对象引用或 null 值:

[Exposed=Window]
  interface Node {
    readonly attribute DOMString? namespaceURI;
    readonly attribute Node? parentNode;
    // ...
  };
  

2.13.28. 序列类型 — sequence<T>

sequence<T> 类型是一种参数化类型,其值为类型 T 的(可能为零长度的)列表

序列始终按值传递。在序列用某种对象表示的语言绑定中,将序列传递给 平台对象不会导致该对象保留对序列的引用。同样,从平台对象返回的任何序列都将是副本,所做的修改将对平台对象不可见。

当从上下文中隐含知道列表被视为序列时,可以使用列表的字面语法来表示序列。然而,在 IDL 片段中没有办法表示常量序列值。

序列不能作为属性常量的类型使用。

注意: 此限制的存在是为了让规范编写者和 API 用户清楚序列是被复制的,而不是引用被传递。建议通过使用一对操作来获取和设置序列,而不是使用可写的序列类型属性。

任何列表都可以被隐式地视为 sequence<T>,只要它只包含类型 T

2.13.29. 异步可迭代类型 — async iterable<T>

一个异步可迭代类型是一个参数化类型,其值是对可以产生类型为 T 的值的异步可迭代、可能无限的序列的对象的引用。

序列(所有值都预先已知的固定长度列表)不同,由异步可迭代对象创建的异步可迭代序列是惰性的。它们的值可能仅在迭代期间异步产生,因此在创建异步可迭代对象时可能不知道值或长度。

在语言绑定中,异步可迭代对象通过引用传递,其中它们由对象表示。这意味着将异步可迭代对象传递给平台对象将导致该对象保留对异步可迭代对象的引用。类似地,从平台对象返回的任何异步可迭代对象都将是对同一对象的引用,并且对其进行的修改将对平台对象可见。这与始终按值传递的序列相反。

注意:异步可迭代对象不能从 IDL 构造。如果从操作返回,或用作字典成员的类型,则异步可迭代对象将源自宿主环境,并通过语言绑定转换为 IDL 类型。操作可能希望返回一个具有异步可迭代声明接口,而不是从 IDL 操作返回异步可迭代对象。

异步可迭代对象不得用作特性常量的类型。

IDL 中没有办法表示异步可迭代值。

2.13.30. 记录类型 — record<K, V>

记录类型 是一种参数化类型,其值为具有 K 实例的有序映射,其K 的实例,V 的实例。K 必须是 DOMStringUSVStringByteString 之一。

当从上下文中隐含知道映射被视为记录时,可以使用有序映射的字面语法来表示记录。然而,在 IDL 片段中没有办法表示常量记录值。

记录总是按值传递。在记录由某种对象表示的语言绑定中,将记录传递给平台对象不会导致该对象保留对记录的引用。类似地,从平台对象返回的任何记录都将是副本,对其进行的修改对平台对象不可见。

记录不得用作特性常量的类型。

任何有序映射都可以隐式地视为 record<K, V>,只要它仅包含其均为 K 类型且其均为 V 类型的条目

2.13.31. Promise 类型 — Promise<T>

promise 类型是一种参数化类型,其值是对对象的引用,这些对象“用作推迟(可能是异步)计算结果的占位符”。有关 promise 对象语义的详细信息,请参阅 JavaScript 规范的第 25.4 节

Promise 类型不可为空,但 T 可以是可空类型。

在 IDL 中没有办法表示 promise 值。

2.13.32. 联合类型

联合类型是一种类型,其值的集合是两个或多个其他类型的联合。联合类型(匹配 UnionType)表示为一系列由 or 关键字分隔的类型,并包含在括号中。构成联合类型的类型称为联合的成员类型

例如,你可以写 (Node 或 DOMString)(double 或 sequence<double>)。当对联合类型应用 ? 后缀时,它位于闭合括号之后,如 (Node 或 DOMString)?

注意,联合类型的成员类型不会深入嵌套的联合类型。因此,对于 (double or (sequence<long> or Event) or (Node or DOMString)?),成员类型是 double(sequence<long> or Event)(Node or DOMString)?

any 类型类似,联合类型的值具有特定类型,即与该值匹配的特定成员类型

扁平化成员类型 是通过以下步骤确定的 联合类型 的类型集:

  1. T联合类型

  2. S 初始化为 ∅。

  3. 对于 T 的每个成员类型 U

    1. 如果 U带注释的类型,则将 U 设置为其内在类型

    2. 如果 U可空类型,则将 U 设置为其内在类型

    3. 如果 U联合类型,则将其扁平化成员类型添加到 S

    4. 否则,U 不是联合类型,将 U 添加到 S

  4. 返回 S

注意: 例如,联合类型 (Node or (sequence<long> or Event) or (XMLHttpRequest or DOMString)? or sequence<(sequence<double> or NodeList)>)扁平化成员类型Nodesequence<long>EventXMLHttpRequestDOMStringsequence<(sequence<double> or NodeList)>

可空成员类型的数量 是通过以下步骤确定的 联合类型 的整数值:

  1. T联合类型

  2. n 初始化为 0。

  3. 对于 T 的每个成员类型 U

    1. 如果 U可空类型,则:

      1. n 加 1。

      2. U 设置为其内在类型

    2. 如果 U联合类型,则:

      1. m可空成员类型的数量

      2. nm

  4. 返回 n

any 类型不能用作 联合成员类型

可空成员类型的数量 必须为 0 或 1。如果为 1,联合类型中也不能包含 字典类型

一个类型包含可空类型,如果:

联合类型中的每对扁平化成员类型TU,必须是可区分的

可以创建bigint数值类型的联合类型。但是,这通常仅应用于如NumberFormat等格式化数值的接口,而不是用于计算。如果创建了这样的联合类型,并将数值类型转换为bigint,则可能会引入精度错误。如果需要使用此功能,请提交问题

一个类型包含undefined,如果:

联合类型的常量值在IDL中表示方式与其成员类型的常量值表示方式相同。

UnionType ::
      ( UnionMemberType or UnionMemberType UnionMemberTypes )
  
UnionMemberType ::
      ExtendedAttributeList DistinguishableType
      UnionType Null
  
UnionMemberTypes ::
      or UnionMemberType UnionMemberTypes
      ε
  
DistinguishableType ::
      PrimitiveType Null
      StringType Null
      identifier Null
      sequence < TypeWithExtendedAttributes > Null
      async iterable < TypeWithExtendedAttributes > Null
      object Null
      symbol Null
      BufferRelatedType Null
      FrozenArray < TypeWithExtendedAttributes > Null
      ObservableArray < TypeWithExtendedAttributes > Null
      RecordType Null
      undefined Null
  

2.13.33. 带注释的类型

可以通过在现有类型上指定某些扩展属性,从现有类型创建额外类型。此类类型称为带注释的类型,它们注释的类型称为内在类型

[Clamp] long定义了一个新的带注释的类型,其行为基于内在类型long,但被[Clamp]扩展属性修改。

以下扩展属性适用于类型:[AllowResizable],[AllowShared],[Clamp],[EnforceRange],以及[LegacyNullToEmptyString]。

与IDL类型type相关联的扩展属性按以下步骤确定:

  1. extended attributes为一个新的空集合

  2. 如果type出现在TypeWithExtendedAttributes的产生式中,追加该产生式中的ExtendedAttributeList中的每个扩展属性extended attributes

    [Exposed=Window]
      interface I {
          attribute [XAttr] long attrib;
          undefined f1(sequence<[XAttr] long> arg);
          undefined f2(optional [XAttr] long arg);
      
          maplike<[XAttr2] DOMString, [XAttr3] long>;
      };
      
      dictionary D {
          required [XAttr] long member;
      };
      
  3. 如果type联合成员类型的成员U追加U关联的每个扩展属性extended attributes

    [Exposed=Window]
      interface I {
          attribute [XAttr] (long or Node) attrib;
      };
      
  4. 如果type直接出现在Type产生式中的Argument产生式中,追加所有适用于类型的扩展属性extended attributes

    [Exposed=Window]
      interface I {
          undefined f([XAttr] long attrib);
      };
      

    注意此示例仅在[XAttr]适用于类型时为此步骤的示例;否则[XAttr]适用于参数,而非参数的类型。

  5. 如果type直接出现在Type产生式中的DictionaryMember产生式中,追加所有适用于类型的扩展属性extended attributes

    dictionary D {
          [XAttr] long member;
      };
      

    注意此示例仅在[XAttr]适用于类型时为此步骤的示例;否则[XAttr]适用于字典成员,而非成员的类型。

  6. 如果typetypedef追加type关联的所有扩展属性extended attributes

    typedef [XAttr] long xlong;
      
  7. 返回extended attributes

对于任何类型,其关联的扩展属性只能包含适用于类型的扩展属性

2.13.34. 缓冲源类型

有多种类型对应于表示数据缓冲区或缓冲区视图的所有可能的非空引用对象的集合。下表列出了这些类型及其表示的缓冲区或视图类型。

类型 缓冲区类型
ArrayBuffer 一个对象,持有指向固定字节数缓冲区的指针(该指针可以为空)
SharedArrayBuffer 一个对象,持有指向共享缓冲区的指针(该指针不能为空),缓冲区的字节数是固定的
DataView 指向缓冲区类型实例的视图,允许按类型访问存储在缓冲区任意偏移量处的整数和浮点数值
Int8Array 指向缓冲区类型实例的视图,将其作为指定位数的二进制补码有符号整数数组进行暴露
Int16Array
Int32Array
BigInt64Array
Uint8Array 指向缓冲区类型实例的视图,将其作为指定位数的无符号整数数组进行暴露
Uint16Array
Uint32Array
BigUint64Array
Uint8ClampedArray 一个缓冲区类型实例的视图,该视图将其公开为具有钳位转换的8 位无符号整数数组
Float16Array 指向缓冲区类型实例的视图,将其作为指定位数的IEEE 754浮点数数组进行暴露;Float16Array对应于ECMAScript提案[PROPOSAL-FLOAT16ARRAY]
Float32Array
Float64Array

注意:这些类型均对应于JavaScript中定义的类。

在IDL中无法表示这些类型的常量值。

在规范文本层面,IDL缓冲源类型只是对对象的引用。要检查或操作缓冲区中的字节,规范文本需要使用§ 3.2.26 缓冲源类型中的算法。

BufferRelatedType ::
  ArrayBuffer
  SharedArrayBuffer
  DataView
  Int8Array
  Int16Array
  Int32Array
  Uint8Array
  Uint16Array
  Uint32Array
  Uint8ClampedArray
  BigInt64Array
  BigUint64Array
  Float16Array
  Float32Array
  Float64Array

2.13.35. 冻结数组类型 — FrozenArray<T>

冻结数组类型 是一种参数化的类型,其值是对持有不可修改值的固定长度数组对象的引用。数组中的值类型为 T

冻结数组类型仅可用于定义在 接口 上的 常规属性静态属性

以下 IDL 片段 定义了一个具有两个冻结数组属性的 接口,一个是 只读 的,另一个不是。
[Exposed=Window]
  interface PersonalPreferences {
      readonly attribute FrozenArray<DOMString> favoriteColors;
      attribute FrozenArray<DOMString> favoriteFoods;
  
      undefined randomizeFavoriteColors();
  };
  

这些属性的行为可以定义如下:

每个 PersonalPreferences 具有关联的喜爱的颜色,一个 FrozenArray<DOMString>,最初等于从 « "purple", "aquamarine" » 中创建一个冻结数组 的结果。

每个 PersonalPreferences 具有关联的喜爱的食物,一个 FrozenArray<DOMString>,最初等于从空列表中创建一个冻结数组的结果。

favoriteColorsgetter 步骤是返回 this 的喜爱的颜色。

favoriteFoodsgetter 步骤是返回 this 的喜爱的食物。

favoriteFoodssetter 步骤是将 this 的喜爱的食物设置为 给定的值

randomizeFavoriteColors()方法步骤如下:

  1. newFavoriteColors 成为表示颜色的两个随机选择的字符串列表。

  2. this 的喜爱的颜色设置为从 newFavoriteColors创建一个冻结数组 的结果。

由于 FrozenArray<T> 值是引用,因此它们不同于序列类型,后者是按值传递的值列表。

在 IDL 中没有表示常量冻结数组值的方法。

2.13.36. 可观察数组类型 — ObservableArray<T>

可观察数组类型 是一种参数化的类型,其值是对类型为 T 的可变对象列表的引用,以及在开发者代码修改该列表内容时执行的行为。

参数化类型 T 不得是 字典类型序列类型记录类型可观察数组类型,但 T 可以为可空类型。

类似于 序列类型冻结数组类型,可观察数组类型围绕 JavaScript 数组类型,强加额外的语义。

可观察数组类型仅可用作定义在 接口 上的 常规属性 的类型。

对于类型为可观察数组的属性,规范作者可以指定一系列算法:

这两个算法都是可选的,如果没有提供,默认行为将是无操作。任何一个算法都可能抛出异常,例如拒绝无效值。

请注意,当 JavaScript 代码将现有索引设置为新值时,这首先会调用 删除索引值 算法以删除现有值,然后调用 设置索引值 算法来设置新值。

每个类型为 可观察数组类型常规属性 都有一个 备份列表,它是一个 列表,初始为空。规范作者可以修改备份列表的内容,这将自动反映在 JavaScript 代码观察到的可观察数组的内容中。同样,JavaScript 代码对可观察数组内容的任何修改将在通过 设置索引值删除索引值 算法后反映回备份列表中。

在 IDL 中无法表示常量的可观察数组值。

以下 IDL 片段 定义了一个具有可观察数组属性的 接口
[Exposed=Window]
  interface Building {
    attribute ObservableArray<Employee> employees;
  };
  

该属性的行为可以定义如下:

Buildingemployees 属性的 设置索引值算法,给定 employeeindex
  1. 如果 employee 今天不允许进入大楼,则抛出 "NotAllowedError" DOMException

  2. 如果 index 大于 200,则抛出 "QuotaExceededError" DOMException

  3. employee 开始工作!

Buildingemployees 属性的 删除索引值算法,给定 employeeindex

  1. 通知安保 employee 已经离开大楼。

然后,JavaScript 代码可以通过多种方式操作 employees 属性:

// 获取一个 Building 的实例。
  const building = getBuilding();
  
  building.employees.push(new Employee("A"));
  building.employees.push(new Employee("B"));
  building.employees.push(new Employee("C"));
  
  building.employees.splice(2, 1);
  const employeeB = building.employees.pop();
  
  building.employees = [new Employee("D"), employeeB, new Employee("C")];
  
  building.employees.length = 0;
  
  // 将会抛出异常:
  building.employees.push("不是 Employee;而是字符串");
  

所有这些操作都会经过上面定义的 设置索引值 算法,在满足所描述的条件时可能抛出异常。 它们还会执行相应的副作用,如在 删除索引值 算法中列出的副作用。

需要注意的是,上述代码示例中的所有 JavaScript 数组方法都可以在可观察数组上使用。它完全像一个 Array 实例一样行为:

const normalArray = [];

  // 如果 building.employees 被定义为索引属性 getter 接口:normalArray
  // 将包含单个项目,即 building.employees。
  //
  // 对于可观察数组(和冻结数组):normalArray 包含 building.employees 内的所有项目。
  normalArray.concat(building.employees);

  // names 是一个 JavaScript 数组。
  const names = building.employees.map(employee => employee.name);

  // 通过了各种品牌检查:
  console.assert(building.employees instanceof Array);
  console.assert(Array.isArray(building.employees));
  console.assert(building.employees.constructor === Array);

  // 即使在 JSON.stringify 中,也被当作数组处理!(注意外部的 []。)
  console.assert(JSON.stringify(building.employees) === `[{}]`);
  

2.14. 扩展属性

一个 扩展属性 是一种注解, 可以出现在 定义注解类型接口成员接口混入成员回调接口成员命名空间成员字典成员操作 参数中, 用于控制语言绑定如何处理这些结构。 扩展属性用 ExtendedAttributeList 指定, 它是用方括号括起来的、逗号分隔的 ExtendedAttribute 列表。

ExtendedAttribute 语法符号几乎可以匹配任何符号序列, 但是本文档定义的 扩展属性 只接受更受限的语法。 在 IDL 片段 中遇到的任何扩展属性 都会与以下五个语法符号进行匹配,以确定它属于哪种形式(或哪些形式):

语法符号 形式 示例
ExtendedAttributeNoArgs 不带参数 [Replaceable]
ExtendedAttributeArgList 带参数列表 目前未使用;以前使用 [Constructor(double x, double y)]
ExtendedAttributeNamedArgList 带命名参数列表 [LegacyFactoryFunction=Image(DOMString src)]
ExtendedAttributeIdent 带标识符 [PutForwards=name]
ExtendedAttributeIdentList 带标识符列表 [Exposed=(Window,Worker)]
ExtendedAttributeWildcard 带通配符 [Exposed=*]

本规范定义了适用于 JavaScript 语言绑定的多个扩展属性,它们在 § 3.3 扩展属性 中进行了描述。 每个扩展属性定义将说明允许的上述五种形式中的哪一种(或多种)。

ExtendedAttributeList ::
      [ ExtendedAttribute ExtendedAttributes ]
      ε
  
ExtendedAttributes ::
      , ExtendedAttribute ExtendedAttributes
      ε
  
ExtendedAttribute ::
      ( ExtendedAttributeInner ) ExtendedAttributeRest
      [ ExtendedAttributeInner ] ExtendedAttributeRest
      { ExtendedAttributeInner } ExtendedAttributeRest
      Other ExtendedAttributeRest
  
ExtendedAttributeRest ::
      ExtendedAttribute
      ε
  
ExtendedAttributeInner ::
      ( ExtendedAttributeInner ) ExtendedAttributeInner
      [ ExtendedAttributeInner ] ExtendedAttributeInner
      { ExtendedAttributeInner } ExtendedAttributeInner
      OtherOrComma ExtendedAttributeInner
      ε
  
Other ::
      integer
      decimal
      identifier
      string
      other
      -
      -Infinity
      .
      ...
      :
      ;
      <
      =
      >
      ?
      *
      ByteString
      DOMString
      FrozenArray
      Infinity
      NaN
      ObservableArray
      Promise
      USVString
      any
      bigint
      boolean
      byte
      double
      false
      float
      long
      null
      object
      octet
      or
      optional
      record
      sequence
      short
      symbol
      true
      unsigned
      undefined
      ArgumentNameKeyword
      BufferRelatedType
  
OtherOrComma ::
      Other
      ,
  
IdentifierList ::
      identifier Identifiers
  
Identifiers ::
      , identifier Identifiers
      ε
  
ExtendedAttributeNoArgs ::
      identifier
  
ExtendedAttributeArgList ::
      identifier ( ArgumentList )
  
ExtendedAttributeIdent ::
      identifier = identifier
  
ExtendedAttributeWildcard ::
      identifier = *
  
ExtendedAttributeIdentList ::
      identifier = ( IdentifierList )
  
ExtendedAttributeNamedArgList ::
      identifier = identifier ( ArgumentList )
  

3. JavaScript 绑定

本节描述了使用 § 2 接口定义语言 编写的定义如何对应于 ECMAScript 语言规范 中的 JavaScript 构造,详见 [ECMA-262]

除非另有规定,本节中定义的对象是普通对象,详见 ECMAScript § 10.1 Ordinary Object Internal Methods and Internal Slots,如果对象是 函数对象,详见 ECMAScript § 10.3 Built-in Function Objects

本节可以重新定义某些对象的内部方法和内部槽。其他规范也可以重写 平台对象 (即 接口 的实例) 的内部方法或内部槽的定义。 这些具有更改语义的对象应按照异质对象的规则处理。

由于覆盖 JavaScript 内部对象方法是一种低级操作,可能会导致对象的行为与普通对象不同, 除非出于安全或兼容性考虑,否则不应使用此功能。 当前用于定义 HTMLAllCollectionLocation 接口。 [HTML]

除非另有规定,本节和其他规范中定义的奇异对象与普通对象具有相同的内部插槽,并且所有未给出替代定义的内部方法均与普通对象的那些相同。

除非另有规定,本节中定义的对象的 [[Extensible]] 内部槽的值为 true

除非另有规定,本节中定义的对象的 [[Prototype]] 内部槽是 %Object.prototype%

本节描述的一些对象定义了一个 类字符串, 这是在 Object.prototype.toString 返回的字符串中包含的字符串。

如果一个对象有一个类字符串 classString,那么该对象在创建时必须有一个属性,其名称为 %Symbol.toStringTag% 符号,其 PropertyDescriptor 为 {[[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true, [[Value]]: classString}。

本节中的算法使用 ECMAScript § 5.2 算法约定中描述的约定,例如步骤和子步骤的使用、数学运算的使用等等。本节还可能引用 ECMA-262 其他部分中定义的抽象操作和表示法。

当一个算法说要抛出一个 SomethingError 时,这意味着在当前领域中构造一个新的 JavaScript SomethingError 对象并将其抛出,就像 ECMA-262 中的算法所做的那样。

注意,算法步骤可以调用其他算法和抽象操作,而不显式处理它们抛出的异常。当算法或抽象操作抛出异常且调用方未显式处理时, 该异常将导致算法结束并向其调用者传播,依此类推。

考虑以下算法:

  1. x 为传递给该算法的 JavaScript 值。

  2. y 为调用 ? ToString(x) 的结果。

  3. 返回 y

由于 ToString 可以抛出异常(例如,如果传递了对象 ({ toString: function() { throw 1 } })), 并且该异常在上述算法中未处理,那么如果抛出了异常,它将导致该算法结束并向其调用者传播,如果有的话。

3.1. JavaScript 环境

在给定的一组 IDL 片段 的 JavaScript 实现中,将存在若干对应于这些 IDL 片段 定义的 JavaScript 对象。 这些对象被称为 初始对象, 包括以下内容:

每个 领域 必须拥有一组唯一的 初始对象,这些对象会在与该领域关联的任何 JavaScript 执行上下文进入之前创建,但在该领域的 全局对象 创建之后。给定领域中所有初始对象的 [[Prototype]] 必须来自该领域。

在 HTML 用户代理中,当创建多个框架或窗口时可以存在多个 领域。 每个框架或窗口都会有自己的一组 初始对象,如下 HTML 文档所示:

<!DOCTYPE html>
  <title>不同的领域</title>
  <iframe id=a></iframe>
  <script>
  var iframe = document.getElementById("a");
  var w = iframe.contentWindow;              // 框架中的全局对象
  
  Object == w.Object;                        // 评估结果为 false,按 ECMA-262 标准
  Node == w.Node;                            // 评估结果为 false
  iframe instanceof w.Node;                  // 评估结果为 false
  iframe instanceof w.Object;                // 评估结果为 false
  iframe.appendChild instanceof Function;    // 评估结果为 true
  iframe.appendChild instanceof w.Function;  // 评估结果为 false
  </script>
  

注意:所有 接口 定义它们在 哪些 领域暴露。 这允许,例如,Web Workers 的 领域 暴露与 Web 页面领域不同的一组支持的接口。

尽管在撰写本文时 JavaScript 规范尚未反映这一点,但每个 JavaScript 对象都必须具有一个 关联的 领域。 目前,尚未完全指定将对象与领域关联的机制。然而,我们注意到,对于 平台对象,关联的领域等于该对象的 相关领域, 而对于非异质 函数对象(即非 可调用 代理,且非绑定函数), 关联的领域等于函数对象的 [[Realm]] 内部槽的值。

3.2. JavaScript 类型映射

本节描述了 IDL 中的类型如何映射到 JavaScript 中的类型。

以下每个小节描述了给定 IDL 类型的值在 JavaScript 中的表示方式。对于每种 IDL 类型,都描述了 JavaScript 值在传递给期望该类型的平台对象时如何转换为 IDL 值,以及该类型的 IDL 值从平台对象返回时如何转换为 JavaScript 值

请注意,以下小节和算法也适用于通过将扩展属性应用于其头部命名的类型而创建的带注解类型

3.2.1. any

由于 IDL any 类型是所有其他 IDL 类型的联合,因此它可以对应于任何 JavaScript 值类型。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL any 值:

  1. 如果 Vundefined,则返回唯一的 undefined IDL 值。

  2. 如果 Vnull,则返回 null object? 引用。

  3. 如果 V 是布尔值,则返回表示相同真值的 boolean 值。

  4. 如果 V 是数字,则返回将 V 转换unrestricted double 的结果。

  5. 如果 V 是 BigInt,则返回将 V 转换bigint 的结果。

  6. 如果 V 是字符串,则返回将 V 转换DOMString 的结果。

  7. 如果 V 是 Symbol,则返回将 V 转换symbol 的结果。

  8. 如果 V 是对象,则返回引用 V 的 IDL object 值。

IDL any 值根据本节其余部分描述的转换 IDL any 值的特定类型的规则转换为 JavaScript 值

3.2.2. undefined

通过返回唯一的 undefined 值(忽略 V),将 JavaScript 值 V 转换为 IDL undefined 值。

唯一的 IDL undefined转换为 JavaScript undefined 值。

3.2.3. boolean

通过运行以下算法,将 JavaScript 值 V 转换为 IDL boolean 值:

  1. x 为计算 ToBoolean(V) 的结果。

  2. 返回表示与 JavaScript 布尔值 x 相同真值的 IDL boolean 值。

IDL booleantrue 转换为 JavaScript true 值,IDL booleanfalse 转换为 JavaScript false 值。

3.2.4. 整数类型

本节中使用的数学运算,包括 ECMAScript § 5.2 算法约定中定义的那些,应理解为对数学实数计算精确的数学结果。

实际上,当 x 是一个数字值时,“对 x 进行运算”是“对表示与 x 相同数值的数学实数进行运算”的简写。

3.2.4.1. byte

通过运行以下算法,将 JavaScript 值 V 转换为 IDL byte 值:

  1. x? ConvertToInt(V, 8, "signed")。

  2. 返回表示与 x 相同数值的 IDL byte 值。

将 IDL byte转换为 JavaScript 值的结果是一个表示与 IDL byte 值相同数值的数字。该数字值将是范围 [−128, 127] 内的整数。

3.2.4.2. octet

通过运行以下算法,将 JavaScript 值 V 转换为 IDL octet 值:

  1. x? ConvertToInt(V, 8, "unsigned")。

  2. 返回表示与 x 相同数值的 IDL octet 值。

将 IDL octet转换为 JavaScript 值的结果是一个表示与 IDL octet 值相同数值的数字。该数字值将是范围 [0, 255] 内的整数。

3.2.4.3. short

通过运行以下算法,将 JavaScript 值 V 转换为 IDL short 值:

  1. x? ConvertToInt(V, 16, "signed")。

  2. 返回表示与 x 相同数值的 IDL short 值。

将 IDL short转换为 JavaScript 值的结果是一个表示与 IDL short 值相同数值的数字。该数字值将是范围 [−32768, 32767] 内的整数。

3.2.4.4. unsigned short

通过运行以下算法,将 JavaScript 值 V 转换为 IDL unsigned short 值:

  1. x? ConvertToInt(V, 16, "unsigned")。

  2. 返回表示与 x 相同数值的 IDL unsigned short 值。

将 IDL unsigned short转换为 JavaScript 值的结果是一个表示与 IDL unsigned short 值相同数值的数字。该数字值将是范围 [0, 65535] 内的整数。

3.2.4.5. long

通过运行以下算法,将 JavaScript 值 V 转换为 IDL long 值:

  1. x? ConvertToInt(V, 32, "signed")。

  2. 返回表示与 x 相同数值的 IDL long 值。

将 IDL long转换为 JavaScript 值的结果是一个表示与 IDL long 值相同数值的数字。该数字值将是范围 [−2147483648, 2147483647] 内的整数。

3.2.4.6. unsigned long

通过运行以下算法,将 JavaScript 值 V 转换为 IDL unsigned long 值:

  1. x? ConvertToInt(V, 32, "unsigned")。

  2. 返回表示与 x 相同数值的 IDL unsigned long 值。

将 IDL unsigned long转换为 JavaScript 值的结果是一个表示与 IDL unsigned long 值相同数值的数字。该数字值将是范围 [0, 4294967295] 内的整数。

3.2.4.7. long long

通过运行以下算法,将 JavaScript 值 V 转换为 IDL long long 值:

  1. x? ConvertToInt(V, 64, "signed")。

  2. 返回表示与 x 相同数值的 IDL long long 值。

将 IDL long long转换为 JavaScript 值的结果是一个数字值,它表示最接近 long long 的数值,如果存在两个同样接近的值,则选择具有偶数有效数的数值。如果 long long 在范围 [−253 + 1, 253 − 1] 内,则该数字将能够精确表示与 long long 相同的值。

3.2.4.8. unsigned long long

通过运行以下算法,将 JavaScript 值 V 转换为 IDL unsigned long long 值:

  1. x? ConvertToInt(V, 64, "unsigned")。

  2. 返回表示与 x 相同数值的 IDL unsigned long long 值。

将 IDL unsigned long long转换为 JavaScript 值的结果是一个数字值,它表示最接近 unsigned long long 的数值,如果存在两个同样接近的值,则选择具有偶数有效数的数值。如果 unsigned long long 小于或等于 253 − 1,则该数字将能够精确表示与 unsigned long long 相同的值。

3.2.4.9. 抽象操作

IntegerPart(n):

  1. rfloor(abs(n))。

  2. 如果 n < 0,则返回 -1 × r

  3. 否则,返回 r

ConvertToInt(V, bitLength, signedness):

  1. 如果 bitLength 是 64,则:

    1. upperBound 为 253 − 1。

    2. 如果 signedness 是 "unsigned",则令 lowerBound 为 0。

    3. 否则令 lowerBound 为 −253 + 1。

      注意:这确保了与 [EnforceRange] 或 [Clamp] 扩展属性关联long long 类型可以在 JavaScript 的 Number 类型中表示为无歧义的整数。

  2. 否则,如果 signedness 是 "unsigned",则:

    1. lowerBound 为 0。

    2. upperBound 为 2bitLength − 1。

  3. 否则:

    1. lowerBound 为 -2bitLength − 1

    2. upperBound 为 2bitLength − 1 − 1。

  4. x? ToNumber(V)。

  5. 如果 x 是 −0,则将 x 设置为 +0。

  6. 如果转换是到与 [EnforceRange] 扩展属性关联的 IDL 类型,则:

    1. 如果 xNaN、+∞ 或 −∞,则抛出 TypeError

    2. x 设置为 IntegerPart(x)。

    3. 如果 x < lowerBoundx > upperBound,则抛出 TypeError

    4. 返回 x

  7. 如果 x 不是 NaN 并且转换是到与 [Clamp] 扩展属性关联的 IDL 类型,则:

    1. x 设置为 min(max(x, lowerBound), upperBound)。

    2. x 四舍五入到最接近的整数,如果它位于两个整数中间,则选择偶数整数,并选择 +0 而不是 −0。

    3. 返回 x

  8. 如果 xNaN、+0、+∞ 或 −∞,则返回 +0。

  9. x 设置为 IntegerPart(x)。

  10. x 设置为 x 2bitLength

  11. 如果 signedness 是 "signed" 且 x ≥ 2bitLength − 1,则返回 x − 2bitLength

  12. 否则,返回 x

3.2.5. float

通过运行以下算法,将 JavaScript 值 V 转换为 IDL float 值:

  1. x? ToNumber(V)。

  2. 如果 xNaN、+∞ 或 −∞,则抛出 TypeError

  3. S 为有限 IEEE 754 单精度浮点值的集合,但不包括 −0,但添加了两个特殊值:2128 和 −2128

  4. yS 中最接近 x 的数字,如果存在两个同样接近的值,则选择具有偶数有效数的数字。(为此目的,两个特殊值 2128 和 −2128 被认为具有偶数有效数。)

  5. 如果 y 是 2128 或 −2128,则抛出 TypeError

  6. 如果 y 是 +0 且 x 是负数,则返回 −0。

  7. 返回 y

将 IDL float转换为 JavaScript 值的结果是表示与 IDL float 值相同数值的数字值。

3.2.6. unrestricted float

通过运行以下算法,将 JavaScript 值 V 转换为 IDL unrestricted float 值:

  1. x? ToNumber(V)。

  2. 如果 xNaN,则返回表示具有位模式 0x7fc00000 [IEEE-754] 的 IEEE 754 NaN 值的 IDL unrestricted float 值。

  3. S 为有限 IEEE 754 单精度浮点值的集合,但不包括 −0,但添加了两个特殊值:2128 和 −2128

  4. yS 中最接近 x 的数字,如果存在两个同样接近的值,则选择具有偶数有效数的数字。(为此目的,两个特殊值 2128 和 −2128 被认为具有偶数有效数。)

  5. 如果 y 是 2128,则返回 +∞。

  6. 如果 y 是 −2128,则返回 −∞。

  7. 如果 y 是 +0 且 x 是负数,则返回 −0。

  8. 返回 y

注意:由于只有一个 JavaScript NaN 值,因此必须将其规范化为特定的单精度 IEEE 754 NaN 值。选择上述 NaN 值仅仅是因为当其位模式解释为32 位无符号整数时,它是具有最低值的静默 NaN。

将 IDL unrestricted float转换为 JavaScript 值的结果是一个数字:

  1. 如果 IDL unrestricted float 值是 NaN,则数字值为 NaN

  2. 否则,数字值是表示与 IDL unrestricted float 值相同数值的那个值。

3.2.7. double

通过运行以下算法,将 JavaScript 值 V 转换为 IDL double 值:

  1. x? ToNumber(V)。

  2. 如果 xNaN、+∞ 或 −∞,则抛出 TypeError

  3. 返回表示与 x 相同数值的 IDL double 值。

将 IDL double转换为 JavaScript 值的结果是表示与 IDL double 值相同数值的数字值。

3.2.8. unrestricted double

通过运行以下算法,将 JavaScript 值 V 转换为 IDL unrestricted double 值:

  1. x? ToNumber(V)。

  2. 如果 xNaN,则返回表示具有位模式 0x7ff8000000000000 [IEEE-754] 的 IEEE 754 NaN 值的 IDL unrestricted double 值。

  3. 返回表示与 x 相同数值的 IDL unrestricted double 值。

注意:由于只有一个 JavaScript NaN 值,因此必须将其规范化为特定的双精度 IEEE 754 NaN 值。选择上述 NaN 值仅仅是因为当其位模式解释为64 位无符号整数时,它是具有最低值的静默 NaN。

将 IDL unrestricted double转换为 JavaScript 值的结果是一个数字:

  1. 如果 IDL unrestricted double 值是 NaN,则数字值为 NaN

  2. 否则,数字值是表示与 IDL unrestricted double 值相同数值的那个值。

3.2.9. bigint

通过运行以下算法,将 JavaScript 值 V 转换为 IDL bigint 值:

  1. x? ToBigInt(V)。

  2. 返回表示与 x 相同数值的 IDL bigint 值。

将 IDL bigint转换为 JavaScript 值的结果是一个 BigInt:

  1. 返回表示与 IDL bigint 值相同数值的 BigInt 值。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL 数字类型 Tbigint 值:

  1. x? ToNumeric(V)。

  2. 如果 x 是 BigInt,则

    1. 返回表示与 x 相同数值的 IDL bigint 值。

  3. 断言:x 是数字

  4. 返回将 x 转换T 的结果。

3.2.10. DOMString

通过运行以下算法,将 JavaScript 值 V 转换为 IDL DOMString 值:

  1. 如果 Vnull 并且转换是到与 [LegacyNullToEmptyString] 扩展属性关联的 IDL 类型,则返回表示空字符串的 DOMString 值。

  2. x? ToString(V)。

  3. 返回表示与 JavaScript 字符串值 x 所表示的相同代码单元序列的 IDL DOMString 值。

将 IDL DOMString转换为 JavaScript 值的结果是表示与 IDL DOMString 所表示的相同代码单元序列的字符串值。

3.2.11. ByteString

通过运行以下算法,将 JavaScript 值 V 转换为 IDL ByteString 值:

  1. x? ToString(V)。

  2. 如果 x 的任何元素的值大于 255,则抛出 TypeError

  3. 返回一个 IDL ByteString 值,其长度为 x 的长度,并且每个元素的值是 x 相应元素的值。

将 IDL ByteString转换为 JavaScript 值的结果是一个字符串值,其长度是 ByteString 的长度,并且其每个元素的值是 ByteString 相应元素的值。

3.2.12. USVString

通过运行以下算法,将 JavaScript 值 V 转换为 IDL USVString 值:

  1. string 为将 V 转换DOMString 的结果。

  2. 返回一个 IDL USVString 值,该值是将 string 转换标量值序列的结果。

将 IDL USVStringS 转换为 JavaScript 值的结果是 S

3.2.13. object

IDL object 值由 JavaScript 对象值表示。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL object 值:

  1. 如果 V 不是对象,则抛出 TypeError

  2. 返回引用与 V 相同对象的 IDL object 值。

将 IDL object转换为 JavaScript 值的结果是表示对 IDL object 所表示的相同对象的引用的对象值。

3.2.14. symbol

IDL symbol 值由 JavaScript Symbol 值表示。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL symbol 值:
  1. 如果 V 不是 Symbol,则抛出 TypeError

  2. 返回引用与 V 相同符号的 IDL symbol 值。

将 IDL symbol转换为 JavaScript 值的结果是表示对 IDL symbol 所表示的相同符号的引用的 Symbol 值。

3.2.15. 接口类型

IDL 接口类型值由 JavaScript 对象值(包括函数对象)表示。

通过运行以下算法(其中 I接口),将 JavaScript 值 V 转换为 IDL 接口类型值:

  1. 如果 V 实现I,则返回表示对该平台对象的引用的 IDL 接口类型值。

  2. 抛出 TypeError

将 IDL 接口类型转换为 JavaScript 值的结果是表示对 IDL 接口类型值所表示的相同对象的引用的对象值。

3.2.16. 回调接口类型

IDL 回调接口类型值由 JavaScript 对象值(包括函数对象)表示。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL 回调接口类型值:

  1. 如果 V 不是对象,则抛出 TypeError

  2. 返回表示对 V 的引用的 IDL 回调接口类型值,其中当前设置对象作为回调上下文

将 IDL 回调接口类型转换为 JavaScript 值的结果是表示对 IDL 回调接口类型值所表示的相同对象的引用的对象值。

3.2.17. 字典类型

IDL 字典类型值由 JavaScript 对象值表示。对象(或其原型链)上的属性对应于字典成员

通过运行以下算法(其中 D字典类型),将 JavaScript 值 jsDict 转换为 IDL 字典类型值:

  1. 如果 jsDict 不是对象并且 jsDict 既不是 undefined 也不是 null,则抛出 TypeError

  2. idlDict 为一个空的有序映射,表示类型为 D 的字典。

  3. dictionaries 为一个列表,其中包含 D 以及 D 的所有继承字典,按从最少派生到最多派生的顺序排列。

  4. 对于 dictionaries 中的每个字典 dictionary,按顺序:

    1. 对于在 dictionary 上声明的每个字典成员 member,按字典顺序:

      1. keymember标识符

      2. 如果 jsDictundefinednull,则:

        1. jsMemberValueundefined

      3. 否则,

        1. jsMemberValue? Get(jsDict, key)。

      4. 如果 jsMemberValue 不是 undefined,则:

        1. idlMemberValue 为将 jsMemberValue 转换为 IDL 值的结果,该 IDL 值的类型是 member 声明的类型。

        2. 设置 idlDict[key] 为 idlMemberValue

      5. 否则,如果 jsMemberValueundefinedmember 具有默认值,则:

        1. idlMemberValuemember 的默认值。

        2. 设置 idlDict[key] 为 idlMemberValue

      6. 否则,如果 jsMemberValueundefined 并且 member必需的,则抛出 TypeError

  5. 返回 idlDict

注意:在 JavaScript 对象上查找字典成员的顺序不一定与对象的属性枚举顺序相同。

通过运行以下算法(其中 D字典),将 IDL 字典值 V 转换为 JavaScript 对象值:

  1. OOrdinaryObjectCreate(%Object.prototype%)。

  2. dictionaries 为一个列表,其中包含 D 以及 D 的所有继承字典,按从最少派生到最多派生的顺序排列。

  3. 对于 dictionaries 中的每个字典 dictionary,按顺序:

    1. 对于在 dictionary 上声明的每个字典成员 member,按字典顺序:

      1. keymember标识符

      2. 如果 V[key] 存在,则:

        1. idlValueV[key]。

        2. value 为将 idlValue 转换为 JavaScript 值的结果。

        3. 执行 ! CreateDataPropertyOrThrow(O, key, value)。

        回想一下,如果 member 具有默认值,则 key 将始终存在V 中。

  4. 返回 O

3.2.18. 枚举类型

IDL 枚举类型由 JavaScript 字符串值表示。

通过以下方式(其中 E枚举),将 JavaScript 值 V 转换为 IDL 枚举类型值:

  1. S 为调用 ? ToString(V) 的结果。

  2. 如果 S 不是 E枚举值之一,则抛出 TypeError

  3. 返回等于 S 的类型为 E 的枚举值。

将 IDL 枚举类型转换为 JavaScript 值的结果是表示与枚举值相同的代码单元序列的字符串值。

3.2.19. 回调函数类型

IDL 回调函数类型由 JavaScript 函数对象表示,但在 [LegacyTreatNonObjectAsNull] 情况下除外,此时它们可以是任何对象。

通过运行以下算法,将 JavaScript 值 V 转换为 IDL 回调函数类型值:

  1. 如果调用 IsCallable(V) 的结果是 false 并且不是因为 V 被分配给类型为可为空回调函数(该回调函数使用 [LegacyTreatNonObjectAsNull] 进行注释)的属性而执行到 IDL 值的转换,则抛出 TypeError

  2. 返回表示对与 V 所表示的对象相同的对象的引用的 IDL 回调函数类型值,其中当前设置对象作为回调上下文

将 IDL 回调函数类型转换为 JavaScript 值的结果是对 IDL 回调函数类型值所表示的相同对象的引用。

3.2.20. 可为空类型 — T?

IDL 可为空类型值由对应于内部 IDL 类型的 JavaScript 类型的值或 JavaScript null 值表示。

通过以下方式将 JavaScript 值 V 转换为 IDL 可为空类型 T? 值(其中 T内部类型):

  1. 如果 V 不是对象,并且由于 V 被分配给类型为可为空回调函数(该回调函数使用 [LegacyTreatNonObjectAsNull] 进行注释)的属性而执行到 IDL 值的转换,则返回 IDL 可为空类型 T?null

  2. 否则,如果 Vundefined,并且 T 包含 undefined,则返回唯一的 undefined 值。

  3. 否则,如果 Vnullundefined,则返回 IDL 可为空类型 T?null

  4. 否则,返回使用内部 IDL 类型 T 的规则转换 V 的结果。

将 IDL 可为空类型转换为 JavaScript 值的结果是:

  1. 如果 IDL 可为空类型 T? 值是 null,则 JavaScript 值为 null

  2. 否则,JavaScript 值是将 IDL 可为空类型转换内部 IDL 类型 T 的结果。

3.2.21. 序列 — sequence<T>

IDL sequence<T> 值由 JavaScript 数组值表示。

通过以下方式将 JavaScript 值 V 转换为 IDL sequence<T> 值:

  1. 如果 V 不是对象,则抛出 TypeError

  2. method? GetMethod(V, %Symbol.iterator%)。

  3. 如果 methodundefined,则抛出 TypeError

  4. 返回从 Vmethod 创建序列的结果。

通过以下方式将类型为 sequence<T> 的 IDL 序列值 S 转换为 JavaScript 数组对象:

  1. nS 的长度。

  2. A 为通过表达式 [] 创建的新数组对象。

  3. 初始化 i 为 0。

  4. i < n 时:

    1. VS 中索引为 i 的值。

    2. E 为将 V 转换为 JavaScript 值的结果。

    3. P 为调用 ! ToString(i) 的结果。

    4. 执行 ! CreateDataPropertyOrThrow(A, P, E)。

    5. 设置 ii + 1。

  5. 返回 A

3.2.21.1. 从可迭代对象创建序列

给定一个可迭代对象 iterable 和一个迭代器获取方法 method,按照以下步骤创建类型为 sequence<T> 的 IDL 值:

  1. iteratorRecord? GetIteratorFromMethod(iterable, method)。

  2. i 初始化为 0。

  3. 重复执行:

    1. next? IteratorStepValue(iteratorRecord)。

    2. 如果 nextdone,则返回类型为 sequence<T> 且长度为 i 的 IDL 序列值,其中索引 j 处的值为 Sj

    3. Si 初始化为 next 转换为类型 T 的 IDL 值的结果。

    4. i 设为 i + 1。

以下接口定义了一个序列类型的属性以及一个带有序列类型参数的操作

[Exposed=Window]
  interface Canvas {
  
    sequence<DOMString> getSupportedImageCodecs();
  
    undefined drawPolygon(sequence<double> coordinates);
    sequence<double> getLastDrawnPolygon();
  
    // ...
  };
  

在该接口的 JavaScript 实现中,使用元素类型为 String 的数组对象表示 sequence<DOMString>,而元素类型为 Number 的数组表示 sequence<double>。数组对象实际上是按值传递的;每次调用 getSupportedImageCodecs() 函数时,都会返回一个新的数组,而每当将数组传递给 drawPolygon 时,调用完成后不会保留对该数组的引用。

// 获取 Canvas 的实例。假设 getSupportedImageCodecs() 返回一个包含两个 DOMString 值的序列:"image/png" 和 "image/svg+xml"。
  var canvas = getCanvas();
  
  // 一个长度为 2 的数组对象。
  var supportedImageCodecs = canvas.getSupportedImageCodecs();
  
  // 结果为 "image/png"。
  supportedImageCodecs[0];
  
  // 每次调用 canvas.getSupportedImageCodecs() 时,都会返回一个新的数组对象。因此修改返回的数组不会影响下一次函数调用的返回值。
  supportedImageCodecs[0] = "image/jpeg";
  
  // 结果为 "image/png"。
  canvas.getSupportedImageCodecs()[0];
  
  // 由于每次调用都会返回一个新的数组对象,因此此比较结果为 false。
  canvas.getSupportedImageCodecs() == canvas.getSupportedImageCodecs();
  
  // 一个数字数组...
  var a = [0, 0, 100, 0, 50, 62.5];
  
  // ...可以传递给需要 sequence<double> 的平台对象。
  canvas.drawPolygon(a);
  
  // 每个元素将通过调用 ToNumber() 转换为 double。因此,下面的调用与前一个调用等效,只不过在 drawPolygon() 返回之前,会弹出 "hi"。
  a = [false, "",
       { valueOf: function() { alert("hi"); return 100; } }, 0,
       "50", new Number(62.5)];
  canvas.drawPolygon(a);
  
  // 修改传递给 drawPolygon() 的数组保证不会影响 Canvas,因为数组实际上是按值传递的。
  a[4] = 20;
  var b = canvas.getLastDrawnPolygon();
  alert(b[4]);    // 这会弹出 "50"。
  

3.2.22. 异步可迭代对象 — async iterable<T>

在 JavaScript 绑定中,IDL 异步可迭代值由具有以下结构体表示:

通过以下方式将 JavaScript 值 V 转换为 IDL async iterable<T> 值:
  1. 如果 V 不是对象,则抛出 TypeError

  2. method? GetMethod(obj, %Symbol.asyncIterator%)。

  3. 如果 methodundefined

    1. 设置 syncMethod? GetMethod(obj, %Symbol.iterator%)。

    2. 如果 syncMethod 是 undefined,抛出 TypeError

    3. 返回一个 IDL 异步可迭代值,其对象设置为 V方法设置为 syncMethod,并且类型设置为 "sync"。

  4. 返回一个 IDL 异步可迭代值,其对象设置为 V方法设置为 method,并且类型设置为 "async"。

通过以下方式将 IDL async iterable<T>V 转换为 JavaScript 对象:
  1. 返回 V对象

3.2.22.1. 迭代异步迭代器

异步可迭代对象不能直接迭代。相反,它首先被打开以创建一个异步迭代器异步迭代器可以被异步迭代以产生值。

异步迭代器是具有以下结构体

打开一个 async iterable<T> iterable

  1. iterator? GetIteratorFromMethod(iterable对象, iterable方法)。

  2. 如果 iterable类型是 "sync",则设置 iteratorCreateAsyncFromSyncIterator(iterator)。

  3. 返回一个异步迭代器值,其底层记录设置为 iterator类型参数设置为 T

获取下一个值,对于异步迭代器 iterator

  1. nextResultIteratorNext(iterator底层记录)。

  2. 如果 nextResult 是一个突然完成,则返回一个被拒绝的 promise,拒绝原因为 nextResult.[[Value]]。

  3. nextPromise一个已解决的 promise,解决值为 nextResult.[[Value]]。

  4. 返回对 nextPromise 作出反应的结果,使用以下完成步骤,给定 iterResult

    1. 如果 iterResult 不是对象,则抛出 TypeError

    2. done? IteratorComplete(iterResult)。

    3. 如果 done 为 true:

      1. 返回迭代结束

    4. 否则:

      1. V? IteratorValue(iterResult)。

      2. value 为将 V 转换为类型为 iterator类型参数的 IDL 值的结果。

      3. 返回 value

关闭一个 async iterator<T> iterator,使用 ECMAScript 值 reason

  1. iteratorRecorditerator底层记录

  2. iteratorObjiteratorRecord.[[Iterator]]。

  3. returnMethodGetMethod(iteratorObj, "return")。

  4. 如果 returnMethod 是一个突然完成,则返回一个被拒绝的 promise,拒绝原因为 returnMethod.[[Value]]。

  5. 如果 returnMethodundefined,则返回一个已解决的 promise,解决值为 undefined

  6. returnResultCall(returnMethod.[[Value]], iteratorObj, « reason »)。

  7. 如果 returnResult 是一个突然完成,则返回一个被拒绝的 promise,拒绝原因为 returnResult.[[Value]]。

  8. returnPromise一个已解决的 promise,解决值为 returnResult.[[Value]]。

  9. 返回对 returnPromise 作出反应的结果,使用以下完成步骤,给定 returnPromiseResult

    1. 如果 returnPromiseResult 不是对象,则抛出 TypeError

    2. 返回 undefined

concatN 是一个操作,它返回一个 promise,该 promise 将使用传递给它的异步可迭代对象产生的所有字符串的串联来完成。一旦异步可迭代对象产生了 maxN 个字符串,它就会停止串联并关闭迭代器。

interface I {
  Promise<DOMString> concatN(async iterable<DOMString> strings, unsigned long maxN);
};

concatN(iterable, maxN) 方法的步骤如下:

  1. promise一个新的 promise

  2. result 为空字符串。

  3. n 为 0。

  4. iterator打开 iterable 的结果。

  5. step 为将用于处理异步可迭代对象的一系列步骤:

    1. next获取下一个值 iterator 的结果。

    2. next 作出反应

      • 如果 next 以值 v 完成:

        1. 如果 v迭代结束,则用 result 解决 promise

        2. 设置 result 为串联 resultv 的结果。

        3. 设置 nn + 1。

        4. 如果 nmaxN,则:

          1. finish 为使用原因 undefined 关闭 iterator 的结果。

          2. finish 作出反应

            • 如果 finish 已完成,则用 result 解决 promise

            • 如果 finish 因原因 r 而被拒绝,则用 r 拒绝 promise

        5. 否则:

          1. 调用 step

      • 如果 next 因原因 r 而被拒绝,则用 r 拒绝 promise

  6. 调用 step

  7. 返回 promise

3.2.23. 记录 — record<K, V>

IDL record<K, V> 值由 JavaScript 对象值表示。

JavaScript 值 O 按如下方式转换为 IDL record<K, V> 值:

  1. 如果 O 不是对象,则抛出 TypeError

  2. resultrecord<K, V> 的一个新的空实例。

  3. keys? O.[[OwnPropertyKeys]]()。

  4. 对于 keys 中的每个 key

    1. desc? O.[[GetOwnProperty]](key)。

    2. 如果 desc 不是 undefined 并且 desc.[[Enumerable]] 是 true

      1. typedKeykey 转换为 IDL 值,类型为 K

      2. value? Get(O, key)。

      3. typedValuevalue 转换为 IDL 值,类型为 V

      4. 设置 result[typedKey] 为 typedValue

        注意:如果 KUSVString 并且 key 包含未配对的代理项,则 typedKey 可能已经存在于 result 中。

  5. 返回 result

IDL record<…>D 按如下方式转换为 JavaScript 值:

  1. resultOrdinaryObjectCreate(%Object.prototype%)。

  2. 对于 D 中的每个 keyvalue

    1. jsKeykey 转换为 JavaScript 值

    2. jsValuevalue 转换为 JavaScript 值

    3. created! CreateDataProperty(result, jsKey, jsValue)。

    4. 断言:createdtrue

  3. 返回 result

将 JavaScript 值 {b: 3, a: 4} 作为 record<DOMString, double> 参数传递时,会生成 IDL 值 «[ "b" → 3, "a" → 4 ]»。

记录只考虑 自有 可枚举 属性,因此给定一个返回其参数的 IDL 操作 record<DOMString, double> identity(record<DOMString, double> arg),以下代码通过其断言:

let proto = {a: 3, b: 4};
  let obj = {__proto__: proto, d: 5, c: 6}
  Object.defineProperty(obj, "e", {value: 7, enumerable: false});
  let result = identity(obj);
  console.assert(result.a === undefined);
  console.assert(result.b === undefined);
  console.assert(result.e === undefined);
  let entries = Object.entries(result);
  console.assert(entries[0][0] === "d");
  console.assert(entries[0][1] === 5);
  console.assert(entries[1][0] === "c");
  console.assert(entries[1][1] === 6);
  

记录键和值可以受到约束,尽管键只能在三种字符串类型之间受到约束。以下转换具有描述的结果:

传递给的类型 结果
{"😞": 1} record<ByteString, double> TypeError
{"\uD83D": 1} record<USVString, double> «[ "\uFFFD" → 1 ]»
{"\uD83D": {hello: "world"}} record<DOMString, double> «[ "\uD83D" → 0 ]»

3.2.24. Promise 类型 — Promise<T>

IDL promise 类型值由 JavaScript PromiseCapability 记录表示。

通过以下方式将 JavaScript 值 V 转换为 IDL Promise<T> 值:

  1. promiseCapability? NewPromiseCapability(%Promise%)。

  2. 执行 ? Call(promiseCapability.[[Resolve]], undefined, « V »)。

  3. 返回 promiseCapability

将 IDL promise 类型转换为 JavaScript 值的结果是 IDL promise 类型所表示的记录的 [[Promise]] 字段的值。

3.2.24.1. 创建和操作 Promise

要在领域 realm创建一个新的 Promise<T>,请执行以下步骤:

  1. constructorrealm.[[Intrinsics]].[[%Promise%]]。

  2. 返回 ? NewPromiseCapability(constructor)。

要在领域 realm 中创建一个类型为 Promise<T>已解决的 promise,其值为 x(类型为 T 的值),请执行以下步骤:

  1. value 为将 x 转换为 JavaScript 值的结果。

  2. constructorrealm.[[Intrinsics]].[[%Promise%]]。

  3. promiseCapability? NewPromiseCapability(constructor)。

  4. 执行 ! Call(promiseCapability.[[Resolve]], undefined, « value »)。

  5. 返回 promiseCapability

要在领域 realm 中创建一个类型为 Promise<T>被拒绝的 promise,原因为 r(一个 JavaScript 值),请执行以下步骤:

  1. constructorrealm.[[Intrinsics]].[[%Promise%]]。

  2. promiseCapability? NewPromiseCapability(constructor)。

  3. 执行 ! Call(promiseCapability.[[Reject]], undefined, « r »)。

  4. 返回 promiseCapability

要用 x(类型为 T 的值)解决一个 Promise<T> p,请执行以下步骤:

  1. 如果未给出 x,则令其为 undefined 值。

  2. value 为将 x 转换为 JavaScript 值的结果。

  3. 执行 ! Call(p.[[Resolve]], undefined, « value »)。

如果 Tundefined,则 x 参数是可选的,允许使用更简单的 "解决 p" 用法。

要用原因 r(一个 JavaScript 值)拒绝一个 Promise<T> p,请执行以下步骤:

  1. 执行 ! Call(p.[[Reject]], undefined, « r »)。

要对一个 Promise<T> promise 作出反应,给定一组或两组要执行的步骤,涵盖 promise 完成、拒绝或两者兼有的情况,请执行以下步骤:

  1. onFulfilledSteps 为给定参数 V 的以下步骤:

    1. value 为将 V 转换为类型为 T 的 IDL 值的结果。

    2. 如果有一组在 promise 完成时要运行的步骤,则令 result 为执行它们的结果,如果 T 不是 undefined,则给定 value。否则,令 resultvalue

    3. 返回 result转换为 JavaScript 值

  2. onFulfilledCreateBuiltinFunction(onFulfilledSteps, « »):

  3. onRejectedSteps 为给定参数 R 的以下步骤:

    1. reason 为将 R 转换为类型为 any 的 IDL 值的结果。

    2. 如果有一组在 promise 被拒绝时要运行的步骤,则令 result 为执行它们的结果,给定 reason。否则,令 result一个被拒绝的 promise,原因为 reason

    3. 返回 result转换为 JavaScript 值

  4. onRejectedCreateBuiltinFunction(onRejectedSteps, « »):

  5. constructorpromise.[[Promise]].[[Realm]].[[Intrinsics]].[[%Promise%]]。

  6. newCapability? NewPromiseCapability(constructor)。

    注意:并非所有调用者都会使用返回的 Promise。实现可能希望在这些情况下避免创建 newCapability

  7. 执行 PerformPromiseThen(promise.[[Promise]], onFulfilled, onRejected, newCapability)。

  8. 返回 newCapability

注意:此算法的行为与 promise.then() 方法非常相似。特别是,如果步骤返回类型为 UPromise<U> 的值,则此算法也会返回一个 Promise<U>

要在 Promise<T> promise 完成时执行某些步骤 steps(这些步骤接受类型为 T 的值),请执行以下步骤:

  1. 返回对 promise 作出反应的结果:

    • 如果 promise 以值 v 完成,则:

      1. v 执行 steps

要在 Promise<T> promise 被拒绝时执行某些步骤 steps(这些步骤接受一个 JavaScript 值),请执行以下步骤:

  1. 返回对 promise 作出反应的结果:

    • 如果 promise 因原因 r 而被拒绝,则:

      1. r 执行 steps

等待所有 Promise<T>promises列表,其中成功步骤 successSteps 接受 T 值的列表,失败步骤 failureSteps 接受拒绝原因 any 值,请执行以下步骤:

  1. fullfilledCount 为 0。

  2. rejected 为 false。

  3. rejectionHandlerSteps 为给定 arg 的以下步骤:

    1. 如果 rejected 为 true,则中止这些步骤。

    2. 设置 rejected 为 true。

    3. 给定 arg 执行 failureSteps

  4. rejectionHandlerCreateBuiltinFunction(rejectionHandlerSteps, « »):

  5. totalpromises大小

  6. 如果 total 为 0,则:

    1. 将微任务排队以执行给定 « » 的 successSteps

    2. 返回。

  7. index 为 0。

  8. result 为包含 total 个 null 值的列表

  9. 对于 promises 中的每个 promise

    1. promiseIndexindex

    2. fulfillmentHandler 为给定 arg 的以下步骤:

      1. 设置 result[promiseIndex] 为 arg

      2. 设置 fullfilledCountfullfilledCount + 1。

      3. 如果 fullfilledCount 等于 total,则执行给定 resultsuccessSteps

    3. fulfillmentHandlerCreateBuiltinFunction(fulfillmentHandler, « »):

    4. 执行 PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler)。

    5. 设置 indexindex + 1。

获取一个用于等待所有 Promise<T>promises列表领域 realm 的 promise,请执行以下步骤:

  1. promise 为在 realm 中类型为 Promise<sequence<T>>一个新的 promise

  2. successSteps 为给定 results 的以下步骤:

    1. results 解决 promise

  3. failureSteps 为给定 reason 的以下步骤:

    1. reason 拒绝 promise

  4. promisessuccessStepsfailureSteps 等待所有

  5. 返回 promise

当您希望聚合多个 promise 的结果,然后从中生成另一个 promise 时,此定义非常有用,其方式与 Promise.all() 函数对 JavaScript 代码的作用相同。如果您不需要生成另一个 promise,那么等待所有可能更好。

标记为已处理一个 Promise<T> promise,请将 promise.[[Promise]].[[PromiseIsHandled]] 设置为 true。

此定义对于您期望其拒绝通常会被忽略的 promise 非常有用;它确保此类 promise 不会导致 unhandledrejection 事件。最常见的用例是 promise 属性,Web 开发人员可能会也可能不会查阅这些属性。例如 writableStreamWriter.closed promise。

3.2.24.2. 示例

delay 是一个操作,它返回一个将在若干毫秒后完成的 promise。 它演示了如何用一行散文简单地解决一个 promise。

interface I {
          Promise<undefined> delay(unrestricted double ms);
        };
        

delay(ms) 方法的步骤如下:

  1. realm对象的相关领域

  2. taskSource 为某个合适的任务源

  3. 如果 ms 是 NaN,则令 ms 为 +0;否则令 msms 和 +0 中的最大值。

  4. p 为在 realm 中的一个新的 promise

  5. 并行运行以下步骤:

    1. 等待 ms 毫秒。

    2. taskSource将任务排队解决 p

  6. 返回 p

validatedDelay 操作delay 函数非常相似,只是它会验证其参数。 这显示了如何在开始任何异步操作之前使用被拒绝的 promise 来表示立即失败。

interface I {
          Promise<undefined> validatedDelay(unrestricted double ms);
        };
        

validatedDelay(ms) 方法的步骤如下:

  1. realm对象的相关领域

  2. taskSource 为某个合适的任务源

  3. 如果 ms 是 NaN,则返回在 realm一个被拒绝的 promise,拒绝原因为 TypeError

  4. 如果 ms < 0,则返回在 realm一个被拒绝的 promise,拒绝原因为 RangeError

  5. p 为在 realm 中的一个新的 promise

  6. 并行运行以下步骤:

    1. 等待 ms 毫秒。

    2. taskSource将任务排队解决 p

  7. 返回 p

addDelay 是一个操作,它在 promise 敲定和返回的 promise 敲定之间添加额外的延迟毫秒数。

interface I {
          Promise<any> addDelay(Promise<any> promise, unrestricted double ms);
        };
        

addDelay(ms, promise) 方法的步骤如下:

  1. realm对象的相关领域

  2. taskSource 为某个合适的任务源

  3. 如果 ms 是 NaN,则令 ms 为 +0;否则令 msms 和 +0 中的最大值。

  4. p 为在 realm 中的一个新的 promise

  5. promise 作出反应

    • 如果 promise 以值 v 完成,则:

      1. 并行运行以下步骤:

        1. 等待 ms 毫秒。

        2. taskSource将任务排队以用 v 解决 p

    • 如果 promise 因原因 r 而被拒绝,则:

      1. 并行运行以下步骤:

        1. 等待 ms 毫秒。

        2. taskSource将任务排队以用 r 拒绝 p

  6. 返回 p

environment.ready 是一个属性,它指示某个环境的某个部分(例如 DOM 文档)何时变为“就绪”。 它演示了如何编码环境异步性。

interface Environment {
          readonly attribute Promise<undefined> ready;
        };
        

每个 Environment 对象都必须有一个就绪 promise,它是一个 Promise<undefined>

ready 属性获取器的步骤如下:

  1. 返回对象的就绪 promise

要在领域 realm 中创建一个 Environment 对象,请执行以下步骤:

  1. taskSource 为某个合适的任务源

  2. environment 为在 realm 中的新的 Environment 对象。

  3. environment就绪 promise 设置为在 realm 中的一个新的 promise

  4. 并行运行以下步骤:

    1. 执行一些异步工作。

    2. 如果 environment 成功就绪,则在 taskSource将任务排队解决 environment就绪 promise

    3. 如果 environment 未能就绪,则在 taskSource将任务排队以用 "NetworkError" DOMException 拒绝 environment就绪 promise

  5. 返回 environment

addBookmark 是一个操作,它请求用户将当前网页添加为书签。 它源自一些迭代设计工作,并演示了一个更真实的场景,即利用环境异步性以及立即拒绝。

interface I {
          Promise<undefined> addBookmark();
        };
        

addBookmark() 方法的步骤如下:

  1. taskSource 为某个合适的任务源

  2. 如果此方法不是由于显式用户操作而调用的,则返回一个被拒绝的 promise,拒绝原因为 "SecurityError" DOMException

  3. 如果文档的操作模式是独立的,则返回一个被拒绝的 promise,拒绝原因为 "NotSupportedError" DOMException

  4. promise一个新的 promise

  5. info 为获取 Web 应用程序元数据的结果。

  6. 并行运行以下步骤:

    1. 使用 info,并以用户代理特定的方式,允许最终用户选择是否要添加书签。

      1. 如果最终用户中止添加书签的请求(例如,他们按了 Escape 键或“取消”按钮),则在 taskSource将任务排队以用 "AbortError" DOMException 拒绝 promise

      2. 否则,在 taskSource将任务排队解决 promise

  7. 返回 promise

[SERVICE-WORKERS] 中的几个地方使用了获取一个用于等待所有batchRequest 演示了其用途的简化版本。 它以 URL 的序列作为输入,并返回一个 promise,该 promise 用于通过获取相应 URL 创建的 Response 对象的序列。 如果任何获取失败,它将返回一个被拒绝的 promise,拒绝原因为该失败。

interface I {
          Promise<sequence<Response>> batchRequest(sequence<USVString> urls);
        };
        

batchRequest(urls) 方法的步骤如下:

  1. responsePromises 为 « »。

  2. 对于 urls 中的每个 url

    1. p 为调用 fetch() 并传入 url 的结果。

    2. p 追加responsePromises

  3. p获取一个用于等待所有并传入 responsePromises 的结果。

  4. 返回 p

3.2.25. 联合类型

IDL 联合类型值由 JavaScript 值表示,这些值对应于联合的成员类型

将 JavaScript 值 V 转换为 IDL 联合类型值的步骤如下:

  1. 如果联合类型包含 undefinedVundefined,则返回唯一的 undefined 值。

  2. 如果联合类型包含可空类型Vnullundefined,则返回 IDL 值 null

  3. types联合类型扁平化成员类型

  4. 如果 Vnullundefined,则:

    1. 如果 types 包含字典类型,则返回将 V 转换为该字典类型的结果。

  5. 如果 V 是平台对象,则:

    1. 如果 types 包含 V 实现接口类型,则返回引用对象 V 的 IDL 值。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  6. 如果 V 是一个对象V 具有 [[ArrayBufferData]] 内部插槽,并且 IsSharedArrayBuffer(V) 为 false,则:

    1. 如果 types 包含 ArrayBuffer,则返回将 V 转换ArrayBuffer 的结果。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  7. 如果 V 是一个对象V 具有 [[ArrayBufferData]] 内部插槽,并且 IsSharedArrayBuffer(V) 为 true,则:

    1. 如果 types 包含 SharedArrayBuffer,则返回将 V 转换SharedArrayBuffer 的结果。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  8. 如果 V 是一个对象V 具有 [[DataView]] 内部插槽,则:

    1. 如果 types 包含 DataView,则返回将 V 转换DataView 的结果。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  9. 如果 V 是一个对象V 具有 [[TypedArrayName]] 内部插槽,则:

    1. 如果 types 包含一个类型化数组类型,其名称是 V 的 [[TypedArrayName]] 内部插槽的值,则返回将 V 转换为该类型的结果。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  10. 如果 IsCallable(V) 为 true,则:

    1. 如果 types 包含回调函数类型,则返回将 V 转换为该回调函数类型的结果。

    2. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  11. 如果 V 是一个对象,则:

    1. 如果 types 包含异步可迭代类型,则

      1. 如果 types 不包含字符串类型V 没有 [[StringData]] 内部插槽,则

        1. asyncMethod? GetMethod(V, %Symbol.asyncIterator%)。

        2. 如果 asyncMethod 不是 undefined,则返回一个 IDL 异步可迭代值,其中对象设置为 V方法设置为 syncMethod类型设置为 "async"。

        3. syncMethod? GetMethod(V, %Symbol.iterator%)。

        4. 如果 syncMethod 不是 undefined,则返回一个 IDL 异步可迭代值,其中对象设置为 V方法设置为 syncMethod类型设置为 "sync"。

    2. 如果 types 包含序列类型,则

      1. method? GetMethod(V, %Symbol.iterator%)。

      2. 如果 method 不是 undefined,则返回从 Vmethod 创建该类型的序列的结果。

    3. 如果 types 包含冻结数组类型,则

      1. method? GetMethod(V, %Symbol.iterator%)。

      2. 如果 method 不是 undefined,则返回从 Vmethod 创建该类型的冻结数组的结果。

    4. 如果 types 包含字典类型,则返回将 V 转换为该字典类型的结果。

    5. 如果 types 包含记录类型,则返回将 V 转换为该记录类型的结果。

    6. 如果 types 包含回调接口类型,则返回将 V 转换为该回调接口类型的结果。

    7. 如果 types 包含 object,则返回引用对象 V 的 IDL 值。

  12. 如果 V 是布尔值,则:

    1. 如果 types 包含 boolean,则返回将 V 转换boolean 的结果。

  13. 如果 V 是数字,则:

    1. 如果 types 包含数字类型,则返回将 V 转换为该数字类型的结果。

  14. 如果 V 是 BigInt,则:

    1. 如果 types 包含 bigint,则返回将 V 转换bigint 的结果。

  15. 如果 types 包含字符串类型,则返回将 V 转换为该类型的结果。

  16. 如果 types 包含数字类型bigint,则返回将 V 转换为该数字类型bigint 的结果。

  17. 如果 types 包含数字类型,则返回将 V 转换为该数字类型的结果。

  18. 如果 types 包含 boolean,则返回将 V 转换boolean 的结果。

  19. 如果 types 包含 bigint,则返回将 V 转换bigint 的结果。

  20. 抛出一个 TypeError

IDL 联合类型值根据本节(§ 3.2 JavaScript 类型映射)中描述的转换 IDL 联合类型值的特定类型的规则转换为 JavaScript 值

3.2.26. 缓冲区源类型

IDL ArrayBuffer 的值由相应 JavaScript 类的对象表示。 如果它没有与 [AllowResizable] 扩展属性关联,则它只能由 JavaScript ArrayBuffer 对象 V 支持,其中 IsResizableArrayBuffer(V) 为 false。

IDL SharedArrayBuffer 的值由相应 JavaScript 类的对象表示。 如果它没有与 [AllowResizable] 扩展属性关联,则它只能由 JavaScript SharedArrayBuffer 对象 V 支持,其中 IsResizableArrayBuffer(V) 为 false。

IDL 缓冲区视图类型的值由相应 JavaScript 类的对象表示,但对这些对象有以下附加限制。

JavaScript 值 V 通过运行以下算法转换为 IDL ArrayBuffer 值:

  1. 如果 V 不是对象, 或者 V 没有 [[ArrayBufferData]] 内部槽, 则抛出 TypeError

  2. 如果 IsSharedArrayBuffer(V) 为 true,则抛出 TypeError

  3. 如果转换不是到与 [AllowResizable] 扩展属性关联的 IDL 类型,并且 IsResizableArrayBuffer(V) 为 true, 则抛出 TypeError

  4. 返回引用与 V 相同对象的 IDL ArrayBuffer 值。

JavaScript 值 V 通过运行以下算法转换为 IDL SharedArrayBuffer 值:

  1. 如果 V 不是对象, 或者 V 没有 [[ArrayBufferData]] 内部槽, 则抛出 TypeError

  2. 如果 IsSharedArrayBuffer(V) 为 false,则抛出 TypeError

  3. 如果转换不是到与 [AllowResizable] 扩展属性关联的 IDL 类型,并且 IsResizableArrayBuffer(V) 为 true, 则抛出 TypeError

  4. 返回引用与 V 相同对象的 IDL SharedArrayBuffer 值。

JavaScript 值 V 通过运行以下算法转换为 IDL DataView 值:

  1. 如果 V 不是对象, 或者 V 没有 [[DataView]] 内部槽, 则抛出 TypeError

  2. 如果转换不是到与 [AllowShared] 扩展属性关联的 IDL 类型,并且 IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) 为 true, 则抛出 TypeError

  3. 如果转换不是到与 [AllowResizable] 扩展属性关联的 IDL 类型,并且 IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) 为 true, 则抛出 TypeError

  4. 返回引用与 V 相同对象的 IDL DataView 值。

JavaScript 值 V 通过运行以下算法转换为 IDL Int8ArrayInt16ArrayInt32ArrayUint8ArrayUint16ArrayUint32ArrayUint8ClampedArrayBigInt64ArrayBigUint64ArrayFloat16ArrayFloat32ArrayFloat64Array 值:

  1. TV 正在转换到的 IDL 类型。

  2. 如果 V 不是对象, 或者 V 没有 [[TypedArrayName]] 内部槽,其值等于 T 的名称, 则抛出 TypeError

  3. 如果转换不是到与 [AllowShared] 扩展属性关联的 IDL 类型,并且 IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) 为 true, 则抛出 TypeError

  4. 如果转换不是到与 [AllowResizable] 扩展属性关联的 IDL 类型,并且 IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) 为 true, 则抛出 TypeError

  5. 返回引用与 V 相同对象的类型为 T 的 IDL 值。

将任何缓冲区源类型的 IDL 值转换为 JavaScript 值的结果是表示对 IDL 值所表示的相同对象的引用的 Object 值。

领域 realm 中从字节序列 bytes 创建一个 ArrayBuffer
  1. jsArrayBuffer? AllocateArrayBuffer(realm.[[Intrinsics]].[[%ArrayBuffer%]], bytes长度)。

  2. arrayBuffer 为将 jsArrayBuffer 转换为类型为 ArrayBuffer 的 IDL 值的结果。

  3. bytes 写入 arrayBuffer

  4. 返回 arrayBuffer

领域 realm 中从字节序列 bytes 创建一个 SharedArrayBuffer
  1. jsSharedArrayBuffer? AllocateSharedArrayBuffer(realm.[[Intrinsics]].[[%SharedArrayBuffer%]], bytes长度)。

  2. sharedArrayBuffer 为将 jsSharedArrayBuffer 转换为类型为 SharedArrayBuffer 的 IDL 值的结果。

  3. bytes 写入 sharedArrayBuffer

  4. 返回 sharedArrayBuffer

领域 realm 中从字节序列 bytes 创建一个 ArrayBufferView 类型:
  1. 断言:如果类型不是 DataView, 则 bytes长度 该类型的元素大小为 0。

  2. arrayBuffer 为从 bytesrealm创建一个 ArrayBuffer 的结果。

  3. jsArrayBuffer 为将 arrayBuffer 转换为 JavaScript 值的结果。

  4. constructorrealm.[[Intrinsics]] 中正在创建的 ArrayBufferView 类型的适当构造函数。

  5. jsView! Construct(constructor, « jsArrayBuffer »)。

  6. 返回将 jsView 转换为给定类型的结果。

给定一个缓冲区源类型实例 bufferSource获取缓冲区源持有的字节副本
  1. jsBufferSource 为将 bufferSource 转换为 JavaScript 值的结果。

  2. jsArrayBufferjsBufferSource

  3. offset 为 0。

  4. length 为 0。

  5. 如果 jsBufferSource 有一个 [[ViewedArrayBuffer]] 内部槽,则:

    1. jsArrayBuffer 设置为 jsBufferSource.[[ViewedArrayBuffer]]。

    2. offset 设置为 jsBufferSource.[[ByteOffset]]。

    3. length 设置为 jsBufferSource.[[ByteLength]]。

  6. 否则:

    1. 断言:jsBufferSource 是一个 ArrayBufferSharedArrayBuffer 对象。

    2. length 设置为 jsBufferSource.[[ArrayBufferByteLength]]。

  7. 如果 IsDetachedBuffer(jsArrayBuffer) 为 true,则返回空的字节序列

  8. bytes 为一个新的字节序列,其长度等于 length

  9. 对于范围 offsetoffset + length − 1(含)中的 i,将 bytes[ioffset] 设置为 GetValueFromBuffer(jsArrayBuffer, i, Uint8, true, Unordered)。

  10. 返回 bytes

缓冲区源类型实例 bufferSource字节长度是以下步骤返回的值:
  1. jsBufferSource 为将 bufferSource 转换为 JavaScript 值的结果。

  2. 如果 jsBufferSource 有一个 [[ViewedArrayBuffer]] 内部槽,则返回 jsBufferSource.[[ByteLength]]。

  3. 返回 jsBufferSource.[[ArrayBufferByteLength]]。

缓冲区源类型实例 bufferSource底层缓冲区是以下步骤返回的值:
  1. 如果 bufferSource 是一个缓冲区类型实例,则返回 bufferSource

  2. jsBufferView 为将 bufferSource 转换为 JavaScript 值的结果。

  3. jsBufferjsBufferView.[[ViewedArrayBuffer]]。

  4. 如果 IsSharedArrayBuffer(jsBuffer) 为 false,则返回将 jsBuffer 转换为类型为 ArrayBuffer 的 IDL 值的结果。

  5. 返回将 jsBuffer 转换为类型为 SharedArrayBuffer 的 IDL 值的结果。

字节序列 bytes 写入一个缓冲区类型实例 arrayBuffer,可选地给定一个startingOffset(默认为 0):
  1. jsArrayBuffer 为将 arrayBuffer 转换为 JavaScript 值的结果。

  2. 断言:bytes长度jsArrayBuffer.[[ArrayBufferByteLength]] − startingOffset

  3. 对于范围 startingOffsetstartingOffset + bytes长度 − 1(含)中的 i,执行 SetValueInBuffer(jsArrayBuffer, i, Uint8, bytes[i - startingOffset], true, Unordered)。

字节序列 bytes 写入一个 ArrayBufferView view,可选地给定一个startingOffset(默认为 0):
  1. jsView 为将 view 转换为 JavaScript 值的结果。

  2. 断言:bytes长度jsView.[[ByteLength]] − startingOffset

  3. 断言:如果 view 不是 DataView,则 bytes长度 view 类型的元素大小为 0。

  4. arrayBuffer 为将 jsView.[[ViewedArrayBuffer]] 转换为类型为 ArrayBuffer 的 IDL 值的结果。

  5. bytes 写入 arrayBuffer,其中 startingOffset 设置为 jsView.[[ByteOffset]] + startingOffset

在编写将数据写入缓冲区源类型实例的规范文本时必须格外小心,因为底层数据很容易被脚本作者或其他 API 在不可预测的时间更改。如果涉及 SharedArrayBuffer 对象,则尤其如此。

对于非共享情况,更推荐的模式是如果可能,首先传输 ArrayBuffer,以确保写入不会与其他修改重叠,然后根据需要将新的 ArrayBuffer 实例提供给作者代码。或者,您可以获取缓冲区源持有的字节副本,修改这些字节,然后使用它们创建一个新的 ArrayBufferArrayBufferView 以返回给作者代码。

分离一个 ArrayBuffer arrayBuffer
  1. jsArrayBuffer 为将 arrayBuffer 转换为 JavaScript 值的结果。

  2. 执行 ? DetachArrayBuffer(jsArrayBuffer)。

如果 jsArrayBuffer 有一个不为 undefined 的 [[ArrayBufferDetachKey]],例如 WebAssembly.Memorybuffer 属性的值,则会抛出异常。[WASM-JS-API-1]

分离一个已经分离的缓冲区是一个空操作。

如果以下步骤返回 true,则缓冲区源类型实例 bufferSource分离的
  1. jsArrayBuffer 为将 bufferSource 转换为 JavaScript 值的结果。

  2. 如果 jsArrayBuffer 有一个 [[ViewedArrayBuffer]] 内部槽,则将 jsArrayBuffer 设置为 jsArrayBuffer.[[ViewedArrayBuffer]]。

  3. 返回 IsDetachedBuffer(jsArrayBuffer)。

如果以下步骤返回 true,则缓冲区源类型实例 bufferSource可转移的
  1. jsArrayBuffer 为将 bufferSource 转换为 JavaScript 值的结果。

  2. 如果 jsArrayBuffer 有一个 [[ViewedArrayBuffer]] 内部槽,则将 jsArrayBuffer 设置为 jsArrayBuffer.[[ViewedArrayBuffer]]。

  3. 如果 IsSharedArrayBuffer(jsArrayBuffer) 为 true,则返回 false。

  4. 如果 IsDetachedBuffer(jsArrayBuffer) 为 true,则返回 false。

  5. 如果 jsArrayBuffer.[[ArrayBufferDetachKey]] 不为 undefined,则返回 false。

  6. 返回 true。

转移一个 ArrayBuffer arrayBuffer,可选地给定一个领域 targetRealm
  1. jsArrayBuffer 为将 arrayBuffer 转换为 JavaScript 值的结果。

  2. 如果 IsDetachedBuffer(jsArrayBuffer) 为 false,则抛出 TypeError

  3. arrayBufferDatajsArrayBuffer.[[ArrayBufferData]]。

  4. arrayBufferByteLengthjsArrayBuffer.[[ArrayBufferByteLength]]。

  5. 执行 ? DetachArrayBuffer(jsArrayBuffer)。

  6. 如果未给定 targetRealm,则令 targetRealm当前领域

  7. jsTransferred? AllocateArrayBuffer(targetRealm.[[Intrinsics]].[[%ArrayBuffer%]], 0)。

  8. jsTransferred.[[ArrayBufferData]] 设置为 arrayBufferData

  9. jsTransferred.[[ArrayBufferByteLength]] 设置为 arrayBufferByteLength

  10. 返回将 jsTransferred 转换为类型为 ArrayBuffer 的 IDL 值的结果。

在以下任何情况下都会抛出异常:
  • arrayBuffer 不能被分离,原因在该算法的定义中解释

  • arrayBuffer 已经分离

  • realm 中无法分配足够的内存。通常只有在 realm 与分配 arrayBuffer代理集群不同时才会出现这种情况。如果它们在同一个代理集群中,则实现将仅更改后备指针以获得相同的可观察结果,同时具有更好的性能且无需分配。

3.2.27. 冻结数组 — FrozenArray<T>

冻结数组类型的值由冻结的 JavaScript Array 对象引用表示。

JavaScript 值 V 通过运行以下算法转换为 IDL FrozenArray<T> 值:

  1. values 为将 V 转换为 IDL 类型 sequence<T> 的结果。

  2. 返回从 values 创建冻结数组的结果。

要从类型为 T 的值序列创建冻结数组,请执行以下步骤:

  1. array 为将类型为 T 的值序列转换为 JavaScript 值的结果。

  2. 执行 ! SetIntegrityLevel(array, "frozen")。

  3. 返回 array

将 IDL FrozenArray<T>转换为 JavaScript 值的结果是表示对 IDL FrozenArray<T> 所表示的相同对象的引用的 Object 值。

3.2.27.1. 从可迭代对象创建冻结数组

要给定一个可迭代对象 iterable 和一个迭代器获取器 method 来创建类型为 FrozenArray<T> 的 IDL 值,请执行以下步骤:

  1. values 为从 iterablemethod 创建类型为 sequence<T> 的序列的结果。

  2. 返回从 values 创建冻结数组的结果。

3.2.28. 可观察数组 — ObservableArray<T>

可观察数组类型的值由可观察数组奇异对象表示。

与通常的转换算法不同,可观察数组类型作为属性获取器属性设置器算法的一部分具有特殊的处理方式。

在 JavaScript 绑定中,表示平台对象的 JavaScript 对象对于可观察数组类型的每个常规属性都有一个后备可观察数组奇异对象。这些对象作为定义属性算法的一部分进行创建和管理。

在 JavaScript 绑定中,给定一个平台对象 obj 和一个属性 attribute,可观察数组属性的后备列表是由以下算法返回的列表
  1. 断言:obj 实现了一个带有常规属性 attribute接口

  2. oaobj 针对 attribute后备可观察数组奇异对象

  3. 返回 oa.[[ProxyHandler]].[[BackingList]]。

3.3. 扩展属性

本节定义了许多扩展属性,它们的存在会影响 JavaScript 绑定。

3.3.1. [AllowResizable]

如果 [AllowResizable] 扩展属性出现在缓冲区类型上,它会创建一个新的 IDL 类型,允许相应的 JavaScript ArrayBufferSharedArrayBuffer 对象可调整大小。

如果 [AllowResizable] 扩展属性出现在缓冲区视图类型之一上,并且 [AllowShared] 扩展属性不存在,它会创建一个新的 IDL 类型,允许 缓冲区视图类型由可调整大小的 JavaScript ArrayBuffer 支持,而不仅仅是固定长度的 ArrayBuffer

如果 [AllowResizable] 扩展属性和 [AllowShared] 扩展属性都出现在缓冲区视图类型之一上,它会创建一个新的 IDL 类型,允许缓冲区 视图类型额外由可增长的 JavaScript SharedArrayBuffer 支持。

[AllowResizable] 扩展属性必须不带参数

不是缓冲区源类型的类型不得与 [AllowResizable] 扩展属性关联

有关使用 [AllowResizable] 所带来的具体要求,请参阅 § 3.2.26 缓冲区源类型中将 JavaScript 值转换为 IDL 缓冲区源类型的规则。

有关 [AllowResizable] 和 [AllowShared] 的用法示例,请参阅 § 3.3.2 [AllowShared] 中的示例

3.3.2. [AllowShared]

如果 [AllowShared] 扩展属性出现在缓冲区视图类型之一上,它会 创建一个新的 IDL 类型,允许对象由 SharedArrayBuffer 支持,而不仅仅是 ArrayBuffer

[AllowShared] 扩展属性必须不带参数

不是缓冲区视图类型的类型不得与 [AllowShared] 扩展属性关联

有关使用 [AllowShared] 所带来的具体要求,请参阅 § 3.2.26 缓冲区源类型中将 JavaScript 值转换为 IDL 缓冲区视图类型的规则。

以下 IDL 片段演示了 [AllowResizable] 和 [AllowShared] 扩展属性的可能组合:
[Exposed=Window]
interface ExampleBufferFeature {
    undefined writeInto(ArrayBufferView dest);
    undefined writeIntoResizable([AllowResizable] ArrayBufferView dest);
    undefined writeIntoShared([AllowShared] ArrayBufferView dest);
    undefined writeIntoSharedResizable([AllowResizable, AllowShared] ArrayBufferView dest);
};

根据此定义,

3.3.3. [Clamp]

如果 [Clamp] 扩展属性出现在整数类型之一上,它将创建一个新的 IDL 类型,当 JavaScript Number 转换为该 IDL 类型时,超出范围的值将被限制在有效值的范围内,而不是使用模运算符(ToInt32ToUint32等)。

[Clamp] 扩展属性必须不带参数

使用 [Clamp] 扩展属性注释的类型不得出现在 只读属性中。不得将某种类型同时与 [Clamp] 和 [EnforceRange] 扩展属性关联。非整数类型的类型不得与 [Clamp] 扩展属性关联

有关将 JavaScript 值转换为各种 IDL 整数类型的规则,请参见 § 3.2.4 整数类型,了解使用 [Clamp] 所涉及的具体要求。

在以下IDL 片段中,声明了两个操作,它们接受三个 octet 参数;一个在所有三个参数上使用了 [Clamp] 扩展属性,而另一个没有:

[Exposed=Window]
  interface GraphicsContext {
    undefined setColor(octet red, octet green, octet blue);
    undefined setColorClamped([Clamp] octet red, [Clamp] octet green, [Clamp] octet blue);
  };
  

调用 setColorClamped 并传入超出 octet 范围的 Number 值时,这些值将被限制在 [0, 255] 范围内。

// 获取一个 GraphicsContext 实例。 
  var context = getGraphicsContext();

  // 调用未使用 [Clamp] 的版本会使用 ToUint8 将 Number 值强制转换为 octet。 
  // 这相当于调用 setColor(255, 255, 1)。 
  context.setColor(-1, 255, 257);

  // 使用一些超出范围的值调用 setColorClamped。 
  // 这相当于调用 setColorClamped(0, 255, 255)。 
  context.setColorClamped(-1, 255, 257);
  

3.3.4. [CrossOriginIsolated]

如果 [CrossOriginIsolated] 扩展属性出现在 接口部分接口接口混合部分接口混合回调接口命名空间部分命名空间接口成员接口混合成员命名空间成员上,它表明该构造仅在其暴露于跨源隔离能力为真的环境内。

[CrossOriginIsolated] 扩展属性必须 不带参数

如果 [CrossOriginIsolated] 出现在重载操作上,则它必须出现在所有重载上。

[CrossOriginIsolated] 扩展属性不得同时出现在

注意:这是因为在成员的包含定义也使用 [CrossOriginIsolated] 扩展属性进行注释时,在该成员上添加 [CrossOriginIsolated] 扩展属性不会进一步限制该成员的暴露。

没有 [CrossOriginIsolated] 扩展属性接口不得继承 另一个确实指定了 [CrossOriginIsolated] 的接口。

以下 IDL 片段定义了一个接口,其中一个操作可从所有上下文执行,另外两个仅可从跨域隔离的上下文执行。

[Exposed=Window]
interface ExampleFeature {
    // 此调用将在所有上下文中成功。
    Promise <Result> calculateNotSoSecretResult();

    // 此操作不会暴露给非隔离上下文。在此类上下文中,
    // ExampleFeature.prototype 上将没有 "calculateSecretResult" 属性。
    [CrossOriginIsolated] Promise<Result> calculateSecretResult();

    // 此处同样适用:该属性不会暴露给非隔离上下文,
    // 在此类上下文中,ExampleFeature.prototype 上将没有 "secretBoolean" 属性。
    [CrossOriginIsolated] readonly attribute boolean secretBoolean;
};

// HighResolutionTimer 不会暴露在非隔离上下文中,其成员也不会。
// 在此类上下文中,Window 上将没有 "HighResolutionTimer" 属性。
[Exposed=Window, CrossOriginIsolated]
interface HighResolutionTimer {
    DOMHighResTimeStamp getHighResolutionTime();
};

// 下面定义的接口混入成员永远不会暴露在非隔离上下文中,
// 无论包含它们的接口是否如此。也就是说,在非隔离上下文中,
// ExampleFeature.prototype 上将没有 "snap" 属性。
[CrossOriginIsolated]
interface mixin Snapshotable {
    Promise<boolean> snap();
};
ExampleFeature includes Snapshotable;

// 另一方面,以下接口混入成员在由不具有
// [CrossOriginIsolated] 扩展属性的主机接口包含时,将暴露给非隔离上下文。
// 也就是说,在非隔离上下文中,ExampleFeature.prototype 上将具有 "log" 属性。
interface mixin Loggable {
    Promise<boolean> log();
};
ExampleFeature includes Loggable;

3.3.5. [Default]

如果 [Default] 扩展属性 出现在 常规操作 上,则表示当操作被调用时,必须执行适当的 默认方法步骤

[Default] 扩展属性必须 不带参数

[Default] 扩展属性不得用于没有定义 默认方法步骤 的任何其他东西。

例如,适合在 toJSON 常规操作 上使用 [Default] 扩展属性:

[Exposed=Window]
  interface Animal {
    attribute DOMString name;
    attribute unsigned short age;
    [Default] object toJSON();
  };
  
  [Exposed=Window]
  interface Human : Animal {
    attribute Dog? pet;
    [Default] object toJSON();
  };
  
  [Exposed=Window]
  interface Dog : Animal {
    attribute DOMString? breed;
  };
  
  

在 JavaScript 语言绑定中,AnimalHuman 和(通过继承)Dog 对象上将存在一个 toJSON() 方法:

// 获取 Human 的一个实例。
  var alice = getHuman();
  
  // 计算结果是一个类似这样的对象(注意 "pet" 此时仍持有 Dog 的实例):
  //
  // {
  //   name: "Alice",
  //   age: 59,
  //   pet: Dog
  // }
  alice.toJSON();
  
  // 计算结果是一个类似这样的对象(注意 "breed" 缺失了,
  // 因为 Dog 接口没有使用默认的 toJSON 步骤):
  //
  // {
  //   name: "Tramp",
  //   age: 6
  // }
  alice.pet.toJSON();
  
  // 计算结果是一个类似这样的字符串:
  // '{"name":"Alice","age":59,"pet":{"name":"Tramp","age":6}}'
  JSON.stringify(alice);
  

3.3.6. [EnforceRange]

如果 [EnforceRange] 扩展属性 出现在 整数类型 上,则它创建一个新的 IDL 类型,使得当 JavaScript 数字转换为 IDL 类型时,超出范围的值将导致抛出异常,而不是使用带有取模运算的操作符 (ToInt32ToUint32 等) 转换为有效值。该数字将在进行范围检查之前向零取整。

[EnforceRange] 扩展属性必须 不带参数

带有 [EnforceRange] 扩展属性的类型不能出现在 只读 属性中。类型不能同时关联 [Clamp] 和 [EnforceRange] 扩展属性。非 整数类型 的类型不能关联 [EnforceRange] 扩展属性。

请参阅 § 3.2 JavaScript 类型映射 中有关将 JavaScript 值转换为各种 IDL 整数类型的规则,了解使用 [EnforceRange] 的具体要求。

在下面的 IDL 片段中,声明了两个 操作,它们接受三个 octet 参数;其中一个使用 [EnforceRange] 扩展属性,另一个没有:

[Exposed=Window]
  interface GraphicsContext {
    undefined setColor(octet red, octet green, octet blue);
    undefined setColorEnforcedRange([EnforceRange] octet red, [EnforceRange] octet green, [EnforceRange] octet blue);
  };
  

在 IDL 的 JavaScript 实现中,调用 setColorEnforcedRange 并传入超出 octet 范围的数字值时,将会抛出异常。

// 获取 GraphicsContext 的一个实例。
  var context = getGraphicsContext();
  
  // 调用非 [EnforceRange] 版本时,使用 ToUint8 将数字强制转换为 octets。
  // 这相当于调用 setColor(255, 255, 1)。
  context.setColor(-1, 255, 257);
  
  // 调用 setColorEnforcedRange 时,数字将向零舍入。
  // 这相当于调用 setColor(0, 255, 255)。
  context.setColorEnforcedRange(-0.9, 255, 255.2);
  
  // 由于即使在舍入后,第一和第三个参数值仍然超出范围,
  // 因此以下操作将抛出 TypeError。
  context.setColorEnforcedRange(-1, 255, 256);
  

3.3.7. [Exposed]

当 [Exposed] 扩展属性 出现在 接口部分接口接口混合部分接口混合回调接口命名空间部分命名空间或单个接口成员接口混合成员命名空间成员时,它表示该构造在该特定的一组全局接口上公开。

[Exposed] 扩展属性必须接受一个标识符接受一个标识符列表接受一个通配符。 提及的每个标识符都必须是某个接口全局名称并且是唯一的。

自身公开集可以是一个有序集合或特殊值 *,定义如下:

如果 [Exposed] 扩展属性 带有标识符 I

自身公开集集合 « I »。

如果 [Exposed] 扩展属性 带有标识符列表 I

自身公开集集合 I

如果 [Exposed] 扩展属性 带有通配符

自身公开集*

[Exposed=*] 需要谨慎使用。它仅适用于 API 不公开重要新功能的情况。如果 API 可能在某些环境中受到限制或禁用,建议显式列出全局接口。

公开集交集 对于构造 C 和接口或 null H 定义如下:

  1. 断言:C接口成员接口混合成员命名空间成员部分接口部分接口混合部分命名空间接口混合

  2. 断言:H接口 或 null。

  3. 如果 H 为 null,返回 C自身公开集

  4. 如果 C自身公开集*,返回 H公开集

  5. 如果 H公开集*,返回 C自身公开集

  6. 返回 C自身公开集H公开集交集

要获取构造 C公开集,请运行以下步骤:
  1. 断言:C 是一个 接口回调接口命名空间接口成员接口混合成员命名空间成员

  2. 如果 C接口混合成员,则将 H 设为 C主接口,否则为 null。

  3. 如果 C接口成员接口混合成员命名空间成员,那么:

    1. 如果 C 上指定了 [Exposed] 扩展属性,则返回 CH公开集交集

    2. C 设置为 C 所声明的 接口部分接口接口混合部分接口混合命名空间部分命名空间

  4. 如果 C部分接口部分接口混合部分命名空间,那么:

    1. 如果 C 上指定了 [Exposed] 扩展属性,则返回 CH公开集交集

    2. C 设置为原始 接口接口混合命名空间定义的 C

  5. 如果 C接口混合,那么:

    1. 断言:H 不为 null。

    2. 如果 C 上指定了 [Exposed] 扩展属性,则返回 CH公开集交集

    3. C 设置为 H

  6. 断言:C接口回调接口命名空间

  7. 断言:C 上指定了 [Exposed] 扩展属性

  8. 返回 C自身公开集

如果 [Exposed] 出现在一个重载的 操作上, 那么它必须在所有重载上相同地出现。

[Exposed] 扩展属性不得同时在接口成员接口混入成员命名空间成员上,以及在该成员声明所在的部分接口部分接口混入部分命名空间定义上指定。

注意:这是因为在部分接口部分接口混入部分命名空间上添加 [Exposed] 扩展属性是为其每个成员添加注解的简写。

如果 [Exposed] 出现在部分接口部分命名空间上, 那么该部分的自身暴露集必须是该部分的原始接口命名空间暴露集的子集。

如果 [Exposed] 出现在接口命名空间成员上, 那么该成员暴露集必须是其所属的接口命名空间暴露集的子集。

如果 [Exposed] 同时出现在部分接口混入及其原始接口混入上, 那么该部分接口混入自身暴露集必须是该接口混入自身暴露集的子集。

如果 [Exposed] 同时出现在接口混入成员及其所属的接口混入上, 那么该接口混入成员自身暴露集必须是该接口混入自身暴露集的子集。

如果接口 X 继承自另一个接口 Y,那么 X暴露集必须是 Y暴露集的子集。

注意:由于接口混入可以被不同的接口包含, 其成员暴露集包含它们的接口的函数。 如果接口混入成员部分接口混入接口混入使用 [Exposed] 扩展属性进行注解, 那么接口混入成员暴露集是相关构造的自身暴露集宿主接口暴露集交集。 否则,它是宿主接口暴露集

一个接口回调接口命名空间成员 construct 在给定的领域 realm 中是暴露的,如果以下步骤返回 true:
  1. 如果 construct暴露集不是 *,并且 realm.[[GlobalObject]] 没有实现一个在 construct暴露集中的接口,则返回 false。

  2. 如果 realm设置对象不是一个安全上下文,并且 construct 在 [SecureContext] 上是有条件暴露的, 则返回 false。

  3. 如果 realm设置对象跨域隔离能力为 false,并且 construct 在 [CrossOriginIsolated] 上是有条件暴露的, 则返回 false。

  4. 返回 true。

一个接口回调接口命名空间成员 construct 在给定的扩展属性 exposure condition 上是有条件暴露的,如果以下步骤返回 true:
  1. 断言:construct 是一个接口回调接口命名空间接口成员接口混入成员命名空间成员

  2. 如果 construct 是一个接口混入成员,则令 Hconstruct宿主接口, 否则为 null。

  3. 如果 construct 是一个接口成员接口混入成员命名空间成员,则:

    1. 如果在 construct 上指定了 exposure condition 扩展属性, 则返回 true。

    2. 否则,将 construct 设置为声明 construct接口部分接口接口混入部分接口混入命名空间部分命名空间

  4. 如果 construct 是一个部分接口部分接口混入部分命名空间,则:

    1. 如果在 construct 上指定了 exposure condition 扩展属性, 则返回 true。

    2. 否则,将 construct 设置为 construct 的原始接口接口混入命名空间定义。

  5. 如果 construct 是一个接口混入,则:

    1. 如果在 construct 上指定了 exposure condition 扩展属性, 则返回 true。

    2. 否则,将 construct 设置为 H

  6. 断言:construct 是一个接口回调接口命名空间

  7. 如果在 construct 上指定了 exposure condition 扩展属性, 则返回 true。

  8. 否则,返回 false。

Note: 由于 JavaScript 全局对象的 相关设置对象 无法改变它是否是 安全上下文跨域隔离功能,实现中决定是否为接口或接口成员创建属性的过程可以在创建初始对象时做出。

参见§ 3.7 接口§ 3.7.5 常量§ 3.7.6 属性§ 3.7.7 操作,了解使用 [Exposed] 的具体要求。

[Exposed] 用来控制 接口回调接口命名空间或单个 接口成员混入成员命名空间成员 是否可以在 Workers、WorkletWindow 或这些的任意组合中使用。

以下 IDL 片段展示了如何实现这一点:

[Exposed=Window, Global=Window]
  interface Window {
    // ...
  };

  // 通过对 SharedWorkerGlobalScope 和 DedicatedWorkerGlobalScope 使用相同的标识符 Worker,可以同时在 [Exposed] 扩展属性中引用它们。
  [Exposed=Worker, Global=Worker]
  interface SharedWorkerGlobalScope : WorkerGlobalScope {
    // ...
  };

  [Exposed=Worker, Global=Worker]
  interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
    // ...
  };

  // Dimensions 可以在 workers 和主线程中使用。
  [Exposed=(Window,Worker)]
  interface Dimensions {
    constructor(double width, double height);
    readonly attribute double width;
    readonly attribute double height;
  };

  // WorkerNavigator 仅在 workers 中可用。在 workers 的全局范围中访问 WorkerNavigator 会返回其接口对象,而在主线程上这样做会抛出 ReferenceError。
  [Exposed=Worker]
  interface WorkerNavigator {
    // ...
  };

  // Node 仅在主线程中可用。在 workers 的全局范围中访问 Node 会抛出 ReferenceError。
  [Exposed=Window]
  interface Node {
    // ...
  };

  // MathUtils 可以在 workers 和主线程中使用。
  [Exposed=(Window,Worker)]
  namespace MathUtils {
    double someComplicatedFunction(double x, double y);
  };

  // WorkerUtils 仅在 workers 中可用。在 workers 的全局范围中访问 WorkerUtils 会返回其命名空间对象,而在主线程上这样做会抛出 ReferenceError。
  [Exposed=Worker]
  namespace WorkerUtils {
    undefined setPriority(double x);
  };

  // NodeUtils 仅在主线程中可用。在 workers 的全局范围中访问 NodeUtils 会抛出 ReferenceError。
  [Exposed=Window]
  namespace NodeUtils {
    DOMString getAllText(Node node);
  };
  

3.3.8. [Global]

如果 [Global] 扩展属性出现在接口上, 它表示实现此接口的对象将用作领域中的全局对象。

[Global] 扩展属性还定义了接口全局名称

如果 [Global] 扩展属性接受一个标识符

« 给定的标识符 »

如果 [Global] 扩展属性接受一个标识符列表

该标识符列表

[Global] 扩展属性必须是上述形式之一。

注意:接口全局名称是可以在 [Exposed] 扩展属性中引用它的标识符。 单个名称可以在多个不同的全局接口之间共享, 从而允许接口更容易地使用 [Exposed] 将自身同时暴露给所有这些接口。 例如,“Worker” 用于引用几种不同类型的与线程相关的全局接口。

对于这些全局接口, 原型链的结构以及与接口成员对应的属性将如何反映在原型对象上, 将不同于其他接口。具体来说:

  1. 任何命名属性都将暴露在原型链中的一个对象上——命名属性对象—— 而不是对象本身上。

  2. 来自接口接口成员将对应于对象本身的属性, 而不是接口原型对象上的属性。

所有领域都有一个全局原型链是否可变布尔值, 它可以在创建领域时设置。 它的值在领域的生命周期内不能更改。 默认情况下设置为 false。

这允许 ShadowRealm 全局对象具有可变原型。

将命名属性放在原型链中的对象上, 是为了使变量声明和裸字赋值 将用全局对象本身的属性来遮蔽命名属性。

将与接口成员对应的属性放在对象本身上, 意味着像下面这样的常用特性检测方法将起作用:

var indexedDB = window.indexedDB || window.webkitIndexedDB ||
                window.mozIndexedDB || window.msIndexedDB;

var requestAnimationFrame = window.requestAnimationFrame ||
                            window.mozRequestAnimationFrame || ...;

由于 JavaScript 中处理变量声明的方式, 上面的代码将导致 window.indexedDBwindow.requestAnimationFrame 求值为 undefined, 因为遮蔽变量属性会在赋值被求值之前就已经创建了。

如果 [Global] 扩展属性 用于接口,则:

如果 [Global] 在部分接口定义上指定, 则该部分接口定义必须是接口定义中定义命名属性获取器的部分。

[Global] 扩展属性不得用于在同一领域中可以有多个对象实现它的接口

注意:这是因为暴露命名属性的命名属性对象位于原型链中, 让多个对象的命名属性暴露在所有这些对象都继承的对象上是没有意义的。

如果一个接口使用 [Global] 扩展属性进行声明, 那么在该接口中,具有相同标识符成员不得超过一个。 在这些接口中,字符串化器也不得超过一个,可迭代声明异步可迭代声明类映射声明类集合声明也不得超过一个。

注意:这是因为接口的所有成员都被扁平化到实现该接口的对象上。

有关使用 [Global] 对命名属性的具体要求,请参阅 § 3.7.4 命名属性对象, 有关与接口成员对应的属性位置的要求,请参阅 § 3.7.5 常量§ 3.7.6 属性§ 3.7.7 操作

Window 接口将 frames 作为属性暴露在 Window 对象上。由于 Window 对象也作为 JavaScript 的全局对象,因此对命名属性的变量声明或赋值将导致它们被新值替换。对于属性的变量声明不会创建替换现有属性的属性。

[Exposed=Window, Global=Window]
interface Window {
  getter any (DOMString name);
  attribute DOMString name;
  // ...
};

以下 HTML 文档说明了如何覆盖 Window 对象上的命名属性,以及当声明与属性同名的变量时,属性的属性不会被替换:

<!DOCTYPE html>
<title>变量声明和 Window 上的赋值</title> 
<iframe name=abc></iframe> 
<!-- 覆盖命名属性 --> 
<script> 
  window.abc;    // 评估为 iframe 的 Window 对象。 
  abc = 1;       // 覆盖命名属性。 
  window.abc;    // 评估为 1。 
</script>

<!-- 保留 IDL 属性的属性 --> 
<script> 
  Window.prototype.def = 2;         // 在原型上放置属性。 
  window.hasOwnProperty("length");  // 评估为 true。 
  length;                           // 评估为 1。 
  def;                              // 评估为 2。 
</script> 
<script> 
  var length;                       // 变量声明保留现有属性。 
  length;                           // 评估为 1。 
  var def;                          // 变量声明创建覆盖属性。 
  def;                              // 评估为 undefined。 
</script> 

3.3.9. [NewObject]

如果 [NewObject] 扩展属性 出现在 常规操作静态操作 操作 上,则它表示在调用该操作时,必须始终返回对新创建对象的引用。

[NewObject] 扩展属性必须 不带参数

[NewObject] 扩展属性只能用于其返回类型接口类型承诺类型常规静态操作

例如,该扩展属性适用于 createElement() 操作,该操作属于 Document 接口,因为每次调用时都会返回一个新对象。[DOM]

[Exposed=Window]
  interface Document : Node {
    [NewObject] Element createElement(DOMString localName);
    // ...
  };
  

3.3.10. [PutForwards]

如果 [PutForwards] 扩展属性 出现在一个 只读 常规属性 声明中,并且该属性的类型是 接口类型,则表示赋值给该属性将具有特定行为。具体来说,赋值会被“转发”到当前引用的对象上的该扩展属性参数所指定的属性。

[PutForwards] 扩展属性必须 带有一个标识符。假设:

则在 J 上必须声明另一个 属性 B,其 标识符N。对实现 I 的对象的属性 A 进行赋值将导致该值被赋予 A 所引用的对象的属性 B

请注意,带有 [PutForwards] 注释的 属性 可以被链接。也就是说,一个带有 [PutForwards] 的属性可以引用另一个带有该扩展属性的属性。链式转发赋值中不得存在循环。如果在跟随转发赋值链时,某个 接口 上的特定属性被多次遇到,则存在循环。

带有 [PutForwards] 的属性不得同时声明为带有 [LegacyLenientSetter] 或 [Replaceable] 扩展属性。

[PutForwards] 扩展属性不得用于非只读属性

[PutForwards] 扩展属性不得用于静态属性

[PutForwards] 扩展属性不得用于在命名空间上声明的属性。

有关如何实现 [PutForwards] ,请参阅属性部分。

以下 IDL 片段 定义了名字和人的接口。[PutForwards] 扩展属性用于 Person 接口的 name 属性,表明对该属性的赋值将导致对 Person 对象的 full 属性进行赋值:

[Exposed=Window]
  interface Name {
    attribute DOMString full;
    attribute DOMString family;
    attribute DOMString given;
  };
  
  [Exposed=Window]
  interface Person {
    [PutForwards=full] readonly attribute Name name;
    attribute unsigned short age;
  };
  

在 JavaScript 绑定中,这将允许对 name 属性进行赋值:

var p = getPerson();           // 获取一个 Person 实例。

  p.name = 'John Citizen';       // 这条语句...
  p.name.full = 'John Citizen';  // ...的行为与此相同。
  

3.3.11. [Replaceable]

如果 [Replaceable] 扩展属性 出现在一个 只读 常规属性 中,则表示在 平台对象 上设置相应的属性将导致在该对象上创建一个具有相同名称的自有属性,该属性具有被赋予的值。这个属性将会遮蔽存在于 接口原型对象上的属性。

[Replaceable] 扩展属性必须 不带参数

带有 [Replaceable] 的属性不得同时声明为带有 [LegacyLenientSetter] 或 [PutForwards] 扩展属性。

[Replaceable] 扩展属性不得用于非只读属性

[Replaceable] 扩展属性不得用于 静态属性

[Replaceable] 扩展属性不得用于声明在 命名空间中的属性。

有关 [Replaceable] 的具体要求,请参阅 § 3.7.6 属性

以下 IDL 片段定义了一个接口,该接口包含一个用于递增计数器的操作,以及一个用于公开计数器值的属性,该属性的初始值为 0:

[Exposed=Window]
  interface Counter {
    [Replaceable] readonly attribute unsigned long value;
    undefined increment();
  };
  

对实现 Counter平台对象value 属性进行赋值将遮蔽与属性对应的属性:

var counter = getCounter();                              // 获取一个 Counter 实例。

  counter.value;                                           // 值为 0。

  counter.hasOwnProperty("value");                         // 结果为 false。
  Object.getPrototypeOf(counter).hasOwnProperty("value");  // 结果为 true。

  counter.increment();
  counter.increment();
  counter.value;                                           // 结果为 2。

  counter.value = 'a';                                     // 用一个与 Counter::value 无关的值遮蔽该属性。

  counter.hasOwnProperty("value");                         // 结果为 true。

  counter.increment();
  counter.value;                                           // 结果为 'a'。

  delete counter.value;                                    // 恢复原始属性。
  counter.value;                                           // 结果为 3。
  

3.3.12. [SameObject]

如果 [SameObject] 扩展属性出现在只读属性上,则表示在获取给定对象上该属性的值时,必须始终返回相同的值。

[SameObject] 扩展属性必须不带参数

[SameObject] 扩展属性只能用于类型为接口类型object只读属性

例如,该扩展属性适用于 implementation 属性,它出现在 Document 接口中,因为对于给定的 Document 对象,总是返回相同的对象。 [DOM]

[Exposed=Window]
  interface Document : Node {
    [SameObject] readonly attribute DOMImplementation implementation;
    // ...
  };
  

3.3.13. [SecureContext]

如果 [安全上下文] 扩展属性 出现在 接口部分接口接口混合部分接口混合回调接口命名空间部分命名空间接口成员接口混合成员,或 命名空间成员, 这表示该结构只在暴露安全上下文中。 [安全上下文] 扩展属性不能用于其他构造中。

[安全上下文] 扩展属性必须 不带任何参数

如果 [安全上下文] 出现在 重载操作上, 那么它必须出现在所有重载上。

[安全上下文] 扩展属性不能同时在

注意:这是因为在 成员 上添加 [安全上下文] 扩展属性,当它的包含定义也带有 [安全上下文] 扩展属性 时,不会进一步限制该 成员 的暴露。

没有 [安全上下文] 扩展属性接口 不能 继承 另一个指定了 [安全上下文] 的接口。

[安全上下文] 不得在 有条件暴露 于 [跨源隔离] 的构造中指定。 (因为每个 跨源隔离 的环境也是 安全上下文。)

以下 IDL 片段定义了一个接口,其中一个操作可从所有上下文执行,另外两个仅可从安全上下文执行。

[Exposed=Window]
  interface ExampleFeature {
    // 此调用将在所有上下文中成功。
    Promise <Result> calculateNotSoSecretResult();
  
    // 此操作不会暴露于非安全上下文。在这样的上下文中,
    // ExampleFeature.prototype 上将不会有 "calculateSecretResult" 属性。
    [SecureContext] Promise<Result> calculateSecretResult();
  
    // 同样,此属性不会暴露于非安全上下文,
    // 在非安全上下文中,ExampleFeature.prototype 上将不会有 "secretBoolean" 属性。
    [SecureContext] readonly attribute boolean secretBoolean;
  };
  
  // HeartbeatSensor 不会暴露在非安全上下文中,其成员也不会。
  // 在这种上下文中,Window 上将不会有 "HeartbeatSensor" 属性。
  [Exposed=Window, SecureContext]
  interface HeartbeatSensor {
    Promise<float> getHeartbeatsPerMinute();
  };
  
  // 以下定义的接口混合成员永远不会暴露于非安全上下文,
  // 无论包含它们的接口是否暴露。也就是说,在非安全上下文中,ExampleFeature.prototype 上将不会有 "snap" 属性。
  [SecureContext]
  interface mixin Snapshotable {
    Promise<boolean> snap();
  };
  ExampleFeature includes Snapshotable;
  
  // 另一方面,当以下接口混合成员由不具有 [SecureContext] 扩展属性的宿主接口包含时,将暴露于非安全上下文。
  // 也就是说,在非安全上下文中,ExampleFeature.prototype 上将有 "log" 属性。
  interface mixin Loggable {
    Promise<boolean> log();
  };
  ExampleFeature includes Loggable;
  

3.3.14. [Unscopable]

如果 [不可作用域] 扩展属性 出现在 常规属性常规操作 上,它表示具有该接口成员的接口的对象不会在任何以其为基础对象的对象环境记录中包含其属性名称。 其结果是在 with 语句中与属性名称匹配的裸标识符不会解析为该属性。 这是通过将属性名称包含在 接口原型对象%Symbol.unscopables% 属性的值中实现的。

[不可作用域] 扩展属性必须 不带任何参数

[不可作用域] 扩展属性不得出现在 常规属性常规操作 以外的任何地方。

[不可作用域] 扩展属性不得用于在 命名空间 中声明的属性。

请参见 § 3.7.3 接口原型对象 以了解使用 [不可作用域] 所涉及的具体要求。

例如,以下 IDL:

[Exposed=Window]
  interface Thing {
    undefined f();
    [Unscopable] g();
  };
  

f 属性可以在 with 语句中通过裸标识符引用, 但 g 属性则不能:

var thing = getThing();  // Thing 的实例
  with (thing) {
    f;                     // 返回一个 Function 对象。
    g;                     // 抛出 ReferenceError。
  }
  

3.4. 旧版扩展属性

本节定义了许多影响 JavaScript 绑定的扩展属性。与 § 3.3 扩展属性 中的不同, 这些属性仅存在于遗留 Web 平台功能中,只有在需要指定遗留 API 的行为时,才应在规范中使用。

认为有正当理由使用这些扩展属性的编辑强烈建议在继续之前通过 提交问题 来讨论。

3.4.1. [旧版工厂函数]

应使用 构造函数操作,而非此功能。

如果 [LegacyFactoryFunction] 扩展属性 出现在 接口 上, 则表示 JavaScript 全局对象将具有一个指定名称的属性,其值是一个函数,该函数可创建实现该接口的对象。 可以在给定接口上出现多个 [LegacyFactoryFunction] 扩展属性。

[LegacyFactoryFunction] 扩展属性必须 带有命名参数列表。 在“=”之后直接出现的 标识符 是 [LegacyFactoryFunction] 的 标识符。 对于接口上的每个 [LegacyFactoryFunction] 扩展属性,可以通过将指定的参数传递给上述属性值的 构造函数 来构造一个实现该接口的对象。

用于遗留工厂函数的 标识符 不得与其他接口上的 [LegacyFactoryFunction] 扩展属性使用的标识符相同, 也不得与 标识符 相同, 还不得与具有 接口对象 的接口的 标识符 相同,且不得是 保留标识符 之一。

[LegacyFactoryFunction] 和 [Global] 扩展属性 不得在同一 接口 上指定。

关于遗留工厂函数的实现详情,请参阅 § 3.7.2 遗留工厂函数

以下 IDL 定义了一个使用 [LegacyFactoryFunction] 扩展属性的接口。

[Exposed=Window,
   LegacyFactoryFunction=Audio(DOMString src)]
  interface HTMLAudioElement : HTMLMediaElement {
    // ...
  };
  

支持该接口的 JavaScript 实现将允许使用 Audio 函数构造 HTMLAudioElement 对象。

typeof Audio;                   // 结果为 'function'。
  
  var a2 = new Audio('a.flac');   // 使用单参数构造函数创建 HTMLAudioElement。
  

作为额外的遗留怪癖,这些工厂函数将具有一个 prototype 属性,其值等于原始接口的 prototype

console.assert(Audio.prototype === HTMLAudioElement.prototype);
  

3.4.2. [旧版宽松设置器]

如果 [LegacyLenientSetter] 扩展属性 出现在 只读 常规属性 上, 则表示该属性的访问器属性将生成一个空操作 setter。这导致在严格模式下对该属性的错误赋值将被忽略,而不是抛出异常。

观察到某些页面的作者试图通过赋值属性来填充一个 IDL 属性, 但即使属性存在,也可能会意外这样做。在严格模式下,这将导致抛出异常,可能会破坏页面。没有 [LegacyLenientSetter] 的话,这可能会阻止浏览器发布该功能。

[LegacyLenientSetter] 扩展属性必须 不带任何参数。 它不得用于除 只读 常规属性 之外的任何事物上。

具有 [LegacyLenientSetter] 扩展属性的属性不得同时使用 [PutForwards] 或 [Replaceable] 扩展属性。

[LegacyLenientSetter] 扩展属性不得用于在 命名空间 中声明的属性。

请参见 属性 部分,了解如何实现 [LegacyLenientSetter]。

以下 IDL 片段定义了一个使用 [LegacyLenientSetter] 扩展属性的接口。

[Exposed=Window]
  interface Example {
    [LegacyLenientSetter] readonly attribute DOMString x;
    readonly attribute DOMString y;
  };
  

支持此接口的 JavaScript 实现将具有一个对应于 x 的访问器属性 setter, 它允许在严格模式下忽略任何赋值。

"use strict";
  
  var example = getExample();  // 获取 Example 的实例。
  
  // 没问题;虽然我们处于严格模式,但有一个空操作的 setter。
  example.x = 1;

  // 抛出 TypeError,因为我们处于严格模式,且没有 setter。
  example.y = 1;
  

3.4.3. [旧版宽松this]

如果 [LegacyLenientThis] 扩展属性 出现在 常规属性 上, 则表示在属性的 getter 或 setter 调用中,如果 this 值不是实现该属性所在的 接口 的对象,则这些调用将被忽略。

[LegacyLenientThis] 扩展属性必须 不带任何参数。 它不能用于 静态属性 上。

[LegacyLenientThis] 扩展属性不得用于在 命名空间 中声明的属性。

请参阅 属性 部分,了解如何实现 [LegacyLenientThis]。

以下 IDL 片段定义了一个使用 [LegacyLenientThis] 扩展属性的接口。

[Exposed=Window]
  interface Example {
    [LegacyLenientThis] attribute DOMString x;
    attribute DOMString y;
  };
  

支持此接口的 JavaScript 实现将允许使用 Example 对象以外的对象调用 x 的 getter 和 setter。

var example = getExample();  // 获取 Example 的实例。
  var obj = { };
  
  // 正常。
  example.x;

  // 被忽略,因为 this 值不是 Example 对象,且使用了 [LegacyLenientThis]。
  Object.getOwnPropertyDescriptor(Example.prototype, "x").get.call(obj);

  // 同样被忽略,因为 Example.prototype 不是 Example 对象,且使用了 [LegacyLenientThis]。
  Example.prototype.x;

  // 抛出 TypeError,因为 Example.prototype 不是 Example 对象。
  Example.prototype.y;
  

3.4.4. [LegacyNamespace]

与其使用此功能,不如使用命名约定为一组接口的接口名称添加特定前缀,作为标识符的一部分,省略中间的点。

如果 [遗留命名空间] 扩展属性 出现在 接口 上,它表示该接口的 接口对象 不会作为全局对象的属性创建, 而是作为由扩展属性参数标识的 命名空间 的属性创建。

[遗留命名空间] 扩展属性必须 带有标识符。 该标识符必须是 标识符,并且是 命名空间 定义的标识符。

[遗留命名空间] 和 [LegacyNoInterfaceObject] 扩展属性不得在同一接口上指定。

有关接口如何在命名空间上暴露的详细信息,请参见 § 3.13.1 命名空间对象

以下 IDL 片段 定义了一个 命名空间 和一个使用 [遗留命名空间] 定义在其中的 接口
namespace Foo { };
  
  [LegacyNamespace=Foo]
  interface Bar {
    constructor();
  };
  

在 JavaScript 实现中,构造函数 Bar 可以这样访问:

var instance = new Foo.Bar();
  

3.4.5. [旧版无接口对象]

如果 [LegacyNoInterfaceObject] 扩展属性 出现在 接口 上, 则表示该接口在 JavaScript 绑定中不会有 接口对象

[LegacyNoInterfaceObject] 扩展属性必须 不带任何参数

[LegacyNoInterfaceObject] 扩展属性不能出现在定义了任何 构造函数静态操作 的接口上。

没有指定 [LegacyNoInterfaceObject] 扩展属性的接口不能继承指定了 [LegacyNoInterfaceObject] 扩展属性的接口。

有关使用 [LegacyNoInterfaceObject] 的具体要求,请参阅 § 3.7 接口

以下 IDL 片段 定义了两个接口, 其中一个接口对象暴露在 JavaScript 全局对象上,而另一个则没有:

[Exposed=Window]
  interface Storage {
    undefined addEntry(unsigned long key, any value);
  };
  
  [Exposed=Window,
   LegacyNoInterfaceObject]
  interface Query {
    any lookupEntry(unsigned long key);
  };
  

上述 IDL 的 JavaScript 实现将允许对 Storage 的原型进行操作,但不允许对 Query 进行操作。

typeof Storage;                        // 结果为 "object"
  
  // 为 Storage.addEntry 添加一些跟踪 alert() 调用。
  var fn = Storage.prototype.addEntry;
  Storage.prototype.addEntry = function(key, value) {
    alert('调用 addEntry()');
    return fn.call(this, key, value);
  };
  
  typeof Query;                          // 结果为 "undefined"
  var fn = Query.prototype.lookupEntry;  // 异常,Query 未定义
  

3.4.6. [旧版Null转为空字符串]

如果 [LegacyNullToEmptyString] 扩展属性 出现在 DOMStringUSVString 类型上, 它将创建一个新的 IDL 类型,使得当 JavaScript 中的 null 被转换为 IDL 类型时, 将以不同于默认方式处理。默认情况下会被转换为 "null",而使用此扩展属性后将转换为空字符串。

[LegacyNullToEmptyString] 扩展属性不得 关联DOMStringUSVString 的类型。

注意:这意味着即使 DOMString? 也不能使用 [LegacyNullToEmptyString], 因为 null 是该类型的有效值。

有关使用 [LegacyNullToEmptyString] 的具体要求,请参阅 § 3.2.10 DOMString

以下 IDL 片段 定义了一个接口, 其中一个属性的类型具有该扩展属性,一个操作的参数类型也具有该扩展属性:
[Exposed=Window]
  interface Dog {
    attribute DOMString name;
    attribute [LegacyNullToEmptyString] DOMString owner;
  
    boolean isMemberOfBreed([LegacyNullToEmptyString] DOMString breedName);
  };
  

实现 Dog 接口的 JavaScript 实现将把分配给 owner 属性或传递给 isMemberOfBreed 函数的 null 值转换为空字符串,而不是 "null":

var d = getDog();         // 假设 d 是实现 Dog 接口的平台对象。
  
  d.name = null;            // 这会将字符串 "null" 分配给 .name 属性。
  
  d.owner = null;           // 这会将空字符串 "" 分配给 .owner 属性。
  
  d.isMemberOfBreed(null);  // 这会将空字符串 "" 传递给 isMemberOfBreed 函数。
  

3.4.7. [旧版覆盖内置]

如果 [LegacyOverrideBuiltIns] 扩展属性 出现在 接口 上, 则表示对于实现该接口的 遗留平台对象,对象的所有 支持的属性名称 将显示在对象上,而不管该对象或其原型链上是否存在其他属性。这意味着命名属性将始终覆盖对象上原本存在的任何属性。 这与通常的行为相反,通常只有当对象本身或其原型链上没有相同名称的属性时,命名属性才会暴露。

[LegacyOverrideBuiltIns] 扩展属性必须 不带任何参数, 并且不得出现在未定义 命名属性 getter 的接口上,也不得与 [Global] 扩展属性一起声明。 如果扩展属性指定在 部分接口 定义上,那么该部分接口定义必须是定义了 命名属性 getter 的接口定义的一部分。

如果 [LegacyOverrideBuiltIns] 扩展属性指定在 部分接口 定义上,则该扩展属性将被视为出现在整个 接口 上。

有关 [LegacyOverrideBuiltIns] 使用的具体要求,请参阅 § 3.9 遗留平台对象§ 3.9.3 [[DefineOwnProperty]]

以下 IDL 片段 定义了两个 接口, 一个具有 命名属性 getter, 另一个没有。

[Exposed=Window]
  interface StringMap {
    readonly attribute unsigned long length;
    getter DOMString lookup(DOMString key);
  };
  
  [Exposed=Window,
   LegacyOverrideBuiltIns]
  interface StringMap2 {
    readonly attribute unsigned long length;
    getter DOMString lookup(DOMString key);
  };
  

在这两个接口的 JavaScript 实现中,获取实现这些接口的对象的某些属性将会得到不同的值:

// 获取 StringMap 的实例。假设它的支持属性名称为 "abc"、"length" 和 "toString"。
  var map1 = getStringMap();
  
  // 这将调用命名属性 getter。
  map1.abc;

  // 这将获取与 length 属性对应的对象上的 "length" 属性。
  map1.length;

  // 这将从对象的原型链中获取 "toString" 属性。
  map1.toString;

  // 获取 StringMap2 的实例。假设它也有 "abc"、"length" 和 "toString" 作为支持属性名称。
  var map2 = getStringMap2();

  // 这将调用命名属性 getter。
  map2.abc;

  // 这也将调用命名属性 getter,尽管对象上的 "length" 属性对应于 length 属性。
  map2.length;

  // 这同样将调用命名属性 getter,尽管 "toString" 是 map2 原型链中的一个属性。
  map2.toString;
  

3.4.8. [旧版视非对象为空值]

如果 [LegacyTreatNonObjectAsNull] 扩展属性 出现在 回调函数上, 则表示分配给类型为 可空回调函数属性 的任何值将被更松散地转换: 如果该值不是对象,则将其转换为 null,并且如果该值不是 可调用,则将其转换为一个在调用时 不执行任何操作的 回调函数

有关使用 [LegacyTreatNonObjectAsNull] 的具体要求,请参阅 § 3.2.20 可空类型 — T?§ 3.2.19 回调函数类型§ 3.12 调用回调函数

以下 IDL 片段 定义了一个接口, 其中一个属性的类型是带有 [LegacyTreatNonObjectAsNull] 注释的 回调函数, 另一个属性的类型是没有 扩展属性回调函数

callback OccurrenceHandler = undefined (DOMString details);
  
  [LegacyTreatNonObjectAsNull]
  callback ErrorHandler = undefined (DOMString details);
  
  [Exposed=Window]
  interface Manager {
    attribute OccurrenceHandler? handler1;
    attribute ErrorHandler? handler2;
  };
  

在 JavaScript 实现中,将不是对象的值(例如 Number 值)或不是 可调用 的值分配给 handler1 和 handler2 时,将会产生不同的行为:

var manager = getManager();  // 获取 Manager 的实例。
  
  manager.handler1 = function() { };
  manager.handler1;            // 结果为该函数。
  
  try {
    manager.handler1 = 123;    // 抛出 TypeError。
  } catch (e) {
  }

  try {
    manager.handler1 = {};     // 抛出 TypeError。
  } catch (e) {
  }

  manager.handler2 = function() { };
  manager.handler2;            // 结果为该函数。
  
  manager.handler2 = 123;
  manager.handler2;            // 结果为 null。
  
  manager.handler2 = {};
  manager.handler2;            // 结果为该对象。
  

3.4.9. [LegacyUnenumerableNamedProperties]

如果 [LegacyUnenumerableNamedProperties] 扩展属性 出现在 支持命名属性的接口 上, 则表示该接口的所有命名属性都是不可枚举的。

[LegacyUnenumerableNamedProperties] 扩展属性必须 不带任何参数, 且不得出现在没有定义 命名属性 getter 的接口上。

如果 [LegacyUnenumerableNamedProperties] 扩展属性在接口上指定,则它适用于所有派生接口,并且不得在它们中的任何一个上指定。

有关使用 [LegacyUnenumerableNamedProperties] 的具体要求,请参阅 § 3.9.1 [[GetOwnProperty]]

3.4.10. [旧版不可伪造]

如果 [LegacyUnforgeable] 扩展属性 出现在 常规属性 或 非 静态 操作上, 则表示该属性或操作将以 JavaScript 属性的形式反映, 其行为不可被修改,并且在对象上执行属性查找时将始终返回该属性的值。 特别是,该属性将是不可配置的,并且作为对象本身的自有属性存在,而不是在其原型链上。

如果一个属性或操作在给定接口 A 上被称为 不可伪造, 则该属性或操作在 A 上被声明,并且带有 [LegacyUnforgeable] 扩展属性

[LegacyUnforgeable] 扩展属性必须 不带任何参数

[LegacyUnforgeable] 扩展属性不得出现在 常规属性 或 非 静态 操作 之外的任何地方。 如果它出现在 操作 上, 则它必须出现在该接口上具有相同 标识符 的所有操作上。

[LegacyUnforgeable] 扩展属性不得用于声明在 命名空间上的属性。

如果一个属性或操作 X 是在接口 A 上的 不可伪造属性或操作, 且 A 是另一个接口 B继承接口之一, 则 B 不得具有与 X 具有相同 标识符常规属性 或 非 静态 操作

例如,以下是不允许的:

[Exposed=Window]
  interface A1 {
    [LegacyUnforgeable] readonly attribute DOMString x;
  };
  [Exposed=Window]
  interface B1 : A1 {
    undefined x();  // 无效;会被 A1 的 x 遮蔽。
  };
  
  [Exposed=Window]
  interface B2 : A1 { };
  B2 includes M1;
  interface mixin M1 {
    undefined x();  // 无效;B2 的 x 会被 A1 的 x 遮蔽。
  };
  

请参阅 § 3.7.6 属性§ 3.7.7 操作§ 3.8 实现接口的平台对象§ 3.9 旧平台对象§ 3.9.3 [[DefineOwnProperty]] 以了解 [LegacyUnforgeable] 的具体使用要求。

以下 IDL 片段 定义了一个包含两个 属性的接口, 其中一个属性被标记为 [LegacyUnforgeable]:

[Exposed=Window]
  interface System {
    [LegacyUnforgeable] readonly attribute DOMString username;
    readonly attribute long long loginTime;
  };
  

在接口的 JavaScript 实现中,username 属性将作为对象本身的不可配置属性暴露:

var system = getSystem();                      // 获取 System 的实例。
  
  system.hasOwnProperty("username");             // 结果为 true。
  system.hasOwnProperty("loginTime");            // 结果为 false。
  System.prototype.hasOwnProperty("username");   // 结果为 false。
  System.prototype.hasOwnProperty("loginTime");  // 结果为 true。
  
  try {
    // 此调用将失败,因为属性不可配置。
    Object.defineProperty(system, "username", { value: "administrator" });
  } catch (e) { }
  
  // 由于 System.prototype.loginTime 是可配置的,因此 defineProperty 调用将成功。
  var forgedLoginTime = 5;
  Object.defineProperty(System.prototype, "loginTime", { value: forgedLoginTime });
  
  system.loginTime;  // 因此现在评估为 forgedLoginTime。
  

3.4.11. [LegacyWindowAlias]

如果 [LegacyWindowAlias] 扩展属性出现在一个接口上, 它表示 Window 接口将为扩展属性中提及的每个标识符拥有一个属性, 其值为该接口的接口对象

[LegacyWindowAlias] 扩展属性必须要么 采用标识符, 要么 采用标识符列表。 在 “=” 之后出现的 标识符 是 [LegacyWindowAlias] 的 标识符

[LegacyWindowAlias] 的每个 标识符 不得与此接口或其他接口上的任何 [LegacyWindowAlias] 扩展属性使用的标识符相同, 不得与此接口或其他接口上的 [LegacyFactoryFunction] 扩展属性使用的 标识符 相同, 也不得与具有 接口对象 的接口的 标识符 相同, 并且不得是 保留标识符 之一。

[LegacyWindowAlias] 和 [LegacyNoInterfaceObject] 扩展属性不得在同一接口上指定。

[LegacyWindowAlias] 和 [LegacyNamespace] 扩展属性不得在同一接口上指定。

[LegacyWindowAlias] 扩展属性不得在接口上指定,如果该接口的暴露集中不包含 Window 接口

一个接口不得有多个 [LegacyWindowAlias] 扩展属性指定。

请参阅 § 3.7 接口 以了解如何实现旧窗口别名。

以下 IDL 定义了一个使用 [LegacyWindowAlias] 扩展属性的接口。

[Exposed=Window,
   LegacyWindowAlias=WebKitCSSMatrix]
  interface DOMMatrix : DOMMatrixReadOnly {
    // ...
  };
  

支持此接口的 JavaScript 实现将在 Window 对象上暴露两个具有相同值和相同特性的属性; 一个用于正常暴露 接口对象, 另一个用于以旧名称暴露它。

WebKitCSSMatrix === DOMMatrix;     // 结果为 true。
  
  var m = new WebKitCSSMatrix();     // 创建一个新对象,实现 DOMMatrix。
  
  m.constructor === DOMMatrix;       // 结果为 true。
  m.constructor === WebKitCSSMatrix; // 结果为 true。
  {}.toString.call(m);               // 结果为 '[object DOMMatrix]'。
  

3.5. 安全性

下面章节中的某些算法被定义为对给定对象执行安全检查。 此检查用于确定是否应允许给定的操作调用或属性访问。 安全检查接受以下三个输入:

  1. 正在执行操作调用或属性访问的 平台对象

  2. 操作或属性的 标识符

  3. 函数对象的类型 – "method"(当它对应于 IDL 操作时),或 "getter" 或 "setter"(当它对应于 IDL 属性的 getter 或 setter 函数时)。

注意: HTML 标准定义了如何执行安全检查。 [HTML]

3.6. 重载解析算法

为了定义如何解析函数调用,定义了 重载解析算法。 它的输入是一个 有效重载集S, 和一个 JavaScript 值的列表,args。其输出是一对, 包含 操作扩展属性S 条目的一个和 IDL 值列表或特殊值 "missing"。该算法的行为如下:

  1. maxargS 中条目中最长的类型列表的长度。

  2. nargs大小

  3. argcount 初始化为 min(maxarg, n)。

  4. S 中删除其类型列表长度不为 argcount 的所有条目。

  5. 如果 S 为空,则 抛出 一个 TypeError

  6. d 初始化为 −1。

  7. method 初始化为 undefined

  8. 如果 S 中有多个条目,则将 d 设置为 区分参数索引, 适用于 S 中的条目。

  9. values 初始化为空列表,其中每个条目将是一个 IDL 值或特殊值 “missing”。

  10. i 初始化为 0。

  11. i 小于 d 时:

    1. Vargs[i]。

    2. typeS 中任一条目类型列表中索引 i 处的类型。

      注意:此时 S 中的所有条目在 索引 i 处具有相同的类型和 可选性值

    3. optionalityS 中任一条目的可选性值列表中索引 i 处的值。

    4. 如果 optionality 为 “optional” 且 Vundefined, 则:

      1. 如果索引 i 处的参数声明了 默认值,则将该默认值追加到 values

      2. 否则,将特殊值 “missing” 追加到 values

    5. 否则,将转换 V 为 IDL 类型 type 的结果附加到 values

    6. i 的值加 1。

  12. 如果 i 等于 d,则:

    1. Vargs[i]。

      注意:这是用于决定选择哪个重载的参数。

    2. 如果 VundefinedS 中有一个条目的可选性值 列表中索引 i 处为 “optional”,则从 S 中删除所有其他条目。

    3. 否则:如果 VnullundefinedS 中有一个条目在其类型列表的索引 i 处具有以下类型之一,

      则从 S 中删除所有其他条目。

    4. 否则:如果 V 是一个平台对象S 中有一个条目在其类型列表的 索引 i 处具有以下类型之一,

      则从 S 中删除所有其他条目。

    5. 否则:如果 V 是一个对象V 有一个 [[ArrayBufferData]] 内部插槽,并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      然后从 S 中删除所有其他条目。

    6. 否则:如果 V 是一个对象V 有一个 [[DataView]] 内部插槽,并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      然后从 S 中删除所有其他条目。

    7. 否则:如果 V 是一个对象V 有一个 [[TypedArrayName]] 内部插槽,并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      然后从 S 中删除所有其他条目。

    8. 否则:如果 IsCallable(V) 为 true,并且在 S 中有一个条目在 i 位置具有以下类型之一,

      然后从 S 中删除所有其他条目。

    9. 否则:如果 V 是一个对象并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      并且以下情况并非全部为真,

      并且在执行以下步骤后,

      1. method? GetMethod(V, %Symbol.asyncIterator%)。

      2. 如果 methodundefined,则将 method 设置为 ? GetMethod(V, %Symbol.iterator%)。

      如果 method 不是 undefined,则从 S 中移除所有其他条目。

    10. 否则:如果 V 是一个对象并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      并且在执行以下步骤后,

      1. method? GetMethod(V, %Symbol.iterator%)。

      如果 method 不是 undefined,则从 S 中移除所有其他条目。

    11. 否则:如果 V 是一个对象并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    12. 否则:如果 V 是一个布尔值并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    13. 否则:如果 V 是一个数字并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    14. 否则:如果 V 是一个 BigInt 并且 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    15. 否则:如果 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    16. 否则:如果 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    17. 否则:如果 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    18. 否则:如果 S 中有一个条目,其类型列表的第 i 个位置具有以下类型之一,

      则从 S 中移除所有其他条目。

    19. 否则:如果 S 中有一个条目,其类型列表的第 i 个位置是 any,则从 S 中移除所有其他条目。

    20. 否则:抛出一个 TypeError

  13. callableS 中唯一条目的 操作扩展属性

  14. 如果 i = dmethod 不是 undefined,则

    1. Vargs[i]。

    2. TS 中剩余条目类型列表中索引 i 处的类型。

    3. 断言:T序列类型

    4. values 追加为使用 Vmethod 创建 类型 T 的序列的结果。

    5. i 设置为 i + 1。

  15. i < argcount 时:

    1. Vargs[i]。

    2. typeS 中剩余条目类型列表中索引 i 处的类型。

    3. optionalityS 中剩余条目中 可选值 列表中索引 i 处的值。

    4. 如果 optionality 是 “optional” 且 Vundefined,则:

      1. 如果索引 i 处的参数声明了 默认值,则将该默认值追加到 values

      2. 否则,将特殊值 “missing” 追加到 values

    5. 否则,将转换 V 为 IDL 类型 type 的结果附加到 values

    6. i 设置为 i + 1。

  16. i 小于 callable 声明的参数数量时:

    1. 如果 callable 的索引 i 处的参数声明了 默认值,则将该默认值追加到 values

    2. 否则,如果 callable 的索引 i 处的参数不是可变参数,则将特殊值 “missing” 追加到 values

    3. i 设置为 i + 1。

  17. 返回 <callable, values>。

重载解析算法同时执行了识别被调用的重载操作、构造函数等,并将 JavaScript 参数值转换为其相应的 IDL 值。非正式地说,它的工作过程如下。

首先,通过考虑传递给函数的 JavaScript 参数的数量来选择有效的重载:

一旦我们有了一组具有正确参数数量的可能重载, JavaScript 值就会从左到右进行转换。 重载限制的性质意味着,如果此时我们有多个可能的重载, 那么参数列表中将会有一个位置用于区分我们将最终选择哪个重载; 这就是区分参数索引

我们首先转换区分参数左侧的参数。 (要求是,区分参数索引左侧的参数必须与相同索引处其他重载中的类型相同。) 然后我们检查在区分参数索引处传入的 JavaScript 值的类型, 以确定它可以对应于哪个 IDL 类型。 这使我们能够选择将最终调用的重载。 如果传入的值是 undefined 并且在此位置有一个带有可选参数的重载, 那么我们将选择该重载。如果对于此处传入的值的类型没有有效的重载, 那么我们抛出一个 TypeError。 通常,对区分参数索引处的值的检查没有任何副作用, 重载解析算法中唯一的副作用是转换 JavaScript 值到 IDL 值的结果。 (当其中一个重载在区分参数索引处具有异步可迭代类型序列类型冻结数组类型时,存在一个例外。 在这种情况下,我们尝试获取 %Symbol.asyncIterator% / %Symbol.iterator% 属性以确定适当的重载,并在继续下一步之前单独执行区分参数的转换。)

此时,我们已经确定了要使用的重载。我们现在转换剩余的参数, 从区分参数开始,再次忽略由于在最后一个可能的参数之后传递而被忽略的任何其他参数。

当将可选参数的 JavaScript 值转换为其等效的 IDL 值时, undefined 将被转换为可选参数的默认值(如果它有的话), 否则转换为特殊值“缺失”。

然而,对应于最后一个可变参数的可选参数不会将 undefined 视为特殊的“缺失”值。 undefined 值会像处理非可选参数一样,被转换为可变参数的类型。

3.7. 接口

对于在给定领域暴露的、且未使用 [LegacyNoInterfaceObject] 或 [LegacyNamespace] 扩展属性声明的每个接口, 在领域全局对象上存在一个对应的属性。 该属性的名称是接口的标识符, 其值是一个称为接口对象的对象。 接口对象的特性在 § 3.7.1 接口对象中描述。

如果在暴露的接口上指定了 [LegacyWindowAlias] 扩展属性, 那么对于 [LegacyWindowAlias] 的 标识符中的每个标识符,在 Window 全局对象上都存在一个对应的属性。 该属性的名称是给定的标识符, 其值是对该接口接口对象的引用。

此外,对于暴露的接口上的每个 [LegacyFactoryFunction] 扩展属性, JavaScript 全局对象上都存在一个对应的属性。 该属性的名称是 [LegacyFactoryFunction] 的 标识符, 其值是一个称为旧版工厂函数的对象, 它允许创建实现该接口的对象。 旧版工厂函数的特性在 § 3.7.2 旧版工厂函数中描述。

一些在本节中定义的 JavaScript 方法将在其开头步骤中执行实现检查,以确保它们在正确类型的对象上被调用,并且该方法在当前上下文中是有效的。

要对接口 interface 执行 对象的实现检查,使用标识符 name 和类型 typejsValue 进行检查:

  1. object? ToObject(jsValue)。

  2. 如果 object 是一个 平台对象,则 执行安全检查,传入:

    • 平台对象 object

    • 标识符 name

    • 类型 type

  3. 如果 object实现 interface,则抛出一个 TypeError

  4. 返回 object

该算法尚未在所有地方一致使用。

3.7.1. 接口对象

给定接口接口对象是一个内置函数对象。 它具有与该接口上定义的常量静态操作相对应的属性, 如 § 3.7.5 常量§ 3.7.7 操作章节所述。

如果接口使用构造函数操作声明, 则可以将接口对象作为构造函数调用以创建实现该接口的对象。 将该接口作为函数调用将抛出异常。

未使用构造函数操作声明的接口接口对象在作为函数和作为构造函数调用时都将抛出异常。

接口接口对象具有一个关联对象,称为接口原型对象。 此对象具有与接口上定义的常规属性常规操作相对应的属性, 并在 § 3.7.3 接口原型对象中有更详细的描述。

注意:由于接口对象是一个函数对象,因此当 typeof 操作符应用于接口对象时将返回 "function"。

接口可能具有覆盖的构造步骤,这可以更改接口对象在调用或构造时的行为。默认情况下,接口没有这样的步骤。

通常,通过定义构造函数操作及其行为来描述构造函数。覆盖的构造步骤仅用于更复杂的情况。编辑者希望使用此功能时,强烈建议在继续之前通过 提交问题进行讨论。

给定接口 I(其标识符id,位于领域 realm 中)的接口对象按如下方式创建

  1. stepsI被覆盖的构造函数步骤(如果存在),否则为以下步骤:

    1. 如果 I 未使用构造函数操作声明, 则抛出一个 TypeError

    2. 如果 NewTargetundefined,则抛出一个 TypeError

    3. args 为传入的参数。

    4. nargs大小

    5. id 为接口 I 的标识符。

    6. 计算有效重载集,针对接口 I标识符id 且参数数量为 n 的构造函数,并令 S 为结果。

    7. 令 <constructor, values> 为将 Sargs 传递给重载解析算法的结果。

    8. object内部创建实现 I 的新对象的结果,使用 realmNewTarget

    9. 使用 object 作为 this 并使用 values 作为参数值,执行 constructor构造函数步骤

    10. Oobject转换为 JavaScript 值

    11. 断言:O 是一个实现 I 的对象。

    12. 断言:O.[[Realm]] 是 realm

    13. 返回 O

  2. constructorProtorealm.[[Intrinsics]].[[%Function.prototype%]]。

  3. 如果 I 继承自某个其他接口 P, 则将 constructorProto 设置为 realmP接口对象

  4. FCreateBuiltinFunction(steps, « [[Unforgeables]] », realm, constructorProto)。

  5. unforgeablesOrdinaryObjectCreate(null)。

  6. 给定 realm,在 unforgeables定义 I 的不可伪造常规操作

  7. 给定 realm,在 unforgeables定义 I 的不可伪造常规属性

  8. F.[[Unforgeables]] 设置为 unforgeables

    注意:此对象永远不会暴露给用户代码。它仅用于确保具有不可伪造成员的接口的所有实例对属性获取器属性设置器操作函数使用相同的 JavaScript 函数对象。

  9. 执行 SetFunctionName(F, id)。

  10. length 为 0。

  11. 如果 I 使用构造函数操作声明,则

    1. 计算有效重载集,针对接口 I标识符id 且参数数量为 0 的构造函数,并令 S 为结果。

    2. length 设置为 S 中条目的最短参数列表的长度。

  12. 执行 SetFunctionLength(F, length)。

  13. proto 为在 realm 中为接口 I 创建接口原型对象的结果。

  14. 执行 ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: proto, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false})。

  15. 定义常量F 上的接口 I,给定 realm

  16. 定义静态属性F 上的接口 I,给定 realm

  17. 定义静态操作F 上的接口 I,给定 realm

  18. 返回 F

3.7.2. 旧版工厂函数

由于一个或多个 [LegacyFactoryFunction] 扩展属性 而存在的 遗留工厂函数 是一个 内置函数对象。 它允许构造实现该接口的对象,其中 [LegacyFactoryFunction] 扩展属性出现在接口上。

给定接口 I领域 realm 中,其标识符id旧版工厂函数按如下方式创建

  1. steps 为以下步骤:

    1. 如果 NewTargetundefined,则抛出一个 TypeError

    2. args 为传入的参数。

    3. nargs大小

    4. 计算有效重载集,针对接口 I标识符id 且参数数量为 n 的旧版工厂函数,并令 S 为结果。

    5. 令 <constructor, values> 为将 Sargs 传递给重载解析算法的结果。

    6. object内部创建实现 I 的新对象的结果,使用 realmNewTarget

    7. 使用 object 作为 this 并使用 values 作为参数值,执行 constructor构造函数步骤

    8. Oobject转换为 JavaScript 值

    9. 断言:O 是一个实现 I 的对象。

    10. 断言:O.[[Realm]] 是 realm

    11. 返回 O

  2. FCreateBuiltinFunction(steps, « », realm)。

  3. 执行 SetFunctionName(F, id)。

  4. 计算有效重载集,针对接口 I标识符id 且参数数量为 0 的旧版工厂函数,并令 S 为结果。

  5. lengthS 中条目的最短参数列表的长度。

  6. 执行 SetFunctionLength(F, length)。

  7. proto接口 Irealm 中的接口原型对象

  8. 执行 ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: proto, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false})。

  9. 返回 F

3.7.3. 接口原型对象

对于定义的每个接口,都将存在一个接口原型对象, 无论该接口是否使用 [LegacyNoInterfaceObject] 扩展属性声明。

给定接口 interface领域 realm接口原型对象按如下方式创建

  1. proto 为 null。

  2. 如果 interface 使用 [Global] 扩展属性声明, 并且 interface 支持命名属性, 则将 proto 设置为interfacerealm 创建命名属性对象的结果。

  3. 否则,如果 interface 声明为继承自另一个接口, 则将 proto 设置为该继承接口realm 中的接口原型对象

  4. 否则,如果 interfaceDOMException 接口, 则将 proto 设置为 realm.[[Intrinsics]].[[%Error.prototype%]]。

  5. 否则,将 proto 设置为 realm.[[Intrinsics]].[[%Object.prototype%]]。

  6. 断言:proto 是一个对象

  7. interfaceProtoObj 为 null。

  8. 如果 realm全局原型链是否可变为 true,则:

    1. interfaceProtoObj 设置为 OrdinaryObjectCreate(proto)。

  9. 否则,如果 interface 使用 [Global] 扩展属性声明,或者 interface 位于使用 [Global] 扩展属性声明的接口的继承接口集合中,则:

    1. interfaceProtoObj 设置为 MakeBasicObject(« [[Prototype]], [[Extensible]] »)。

    2. interfaceProtoObj.[[Prototype]] 设置为 proto

    3. interfaceProtoObj 特有的不可变原型奇异对象的内部方法设置为 ECMA-262 不可变原型奇异对象中指定的定义。

  10. 否则,将 interfaceProtoObj 设置为 OrdinaryObjectCreate(proto)。

  11. 如果 interface 具有任何使用 [Unscopable] 扩展属性声明的成员, 则:

    1. unscopableObjectOrdinaryObjectCreate(null)。

    2. 对于 interface 中每个使用 [Unscopable] 扩展属性声明的暴露的成员 member

      1. idmember标识符

      2. 执行 ! CreateDataPropertyOrThrow(unscopableObject, id, true)。

    3. desc 为 PropertyDescriptor{[[Value]]: unscopableObject, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}。

    4. 执行 ! DefinePropertyOrThrow(interfaceProtoObj, %Symbol.unscopables%, desc)。

  12. 如果 interface 未使用 [Global] 扩展属性声明,则:

    1. 给定 realm,在 interfaceProtoObj定义 interface 的常规属性

    2. 给定 realm,在 interfaceProtoObj定义 interface 的常规操作

    3. 给定 realm,在 interfaceProtoObj定义 interface 的迭代方法

    4. 给定 realm,在 interfaceProtoObj定义 interface 的异步迭代方法

  13. 给定 realm,在 interfaceProtoObj定义 interface 的常量

  14. 如果 interface 上未指定 [LegacyNoInterfaceObject] 扩展属性,则:

    1. constructorrealminterface接口对象

    2. desc 为 PropertyDescriptor{[[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true, [[Value]]: constructor}。

    3. 执行 ! DefinePropertyOrThrow(interfaceProtoObj, "constructor", desc)。

  15. 返回 interfaceProtoObj

此外,接口原型对象从以下位置声明性地获取属性:

改为命令式地定义这些属性。

使用 [LegacyNoInterfaceObject] 扩展属性定义的接口接口原型对象将是可访问的。 例如,对于以下 IDL:

[Exposed=Window,
    LegacyNoInterfaceObject]
interface Foo {
};

partial interface Window {
    attribute Foo foo;
};

无法通过接口对象访问接口原型对象(因为它不存在于 window.Foo)。 然而,Foo 的实例可以通过调用其 [[GetPrototypeOf]] 内部方法来暴露接口原型对象—— 在此示例中为 Object.getPrototypeOf(window.foo)

接口原型对象类字符串接口限定名称

3.7.4. 命名属性对象

对于每个使用 [Global] 扩展属性声明且支持命名属性接口, 都将存在一个称为该接口的命名属性对象的对象,命名属性在该对象上公开。

给定接口 interface领域 realm命名属性对象, 按如下方式创建
  1. proto 为 null。

  2. 如果 interface 声明为继承自另一个接口, 则将 proto 设置为继承接口realm 中的接口原型对象

  3. 否则,将 proto 设置为 realm.[[Intrinsics]].[[%Object.prototype%]]。

  4. objMakeBasicObject(« [[Prototype]], [[Extensible]] »)。

  5. 按照 § 3.7.4.1 [[GetOwnProperty]] 中的规定设置 obj.[[GetOwnProperty]]。

  6. 按照 § 3.7.4.2 [[DefineOwnProperty]] 中的规定设置 obj.[[DefineOwnProperty]]。

  7. 按照 § 3.7.4.3 [[Delete]] 中的规定设置 obj.[[Delete]]。

  8. 按照 § 3.7.4.4 [[SetPrototypeOf]] 中的规定设置 obj.[[SetPrototypeOf]]。

  9. 按照 § 3.7.4.5 [[PreventExtensions]] 中的规定设置 obj.[[PreventExtensions]]。

  10. obj.[[Prototype]] 设置为 proto

  11. 返回 obj

注意:命名属性对象的 [[OwnPropertyKeys]] 内部方法 继续使用 OrdinaryOwnPropertyKeys, 这与旧版平台对象的对应方法不同。 由于命名属性不是“真正的”自有属性, 因此该内部方法不会返回它们。

命名属性对象类字符串接口标识符和字符串 "Properties" 的串联。

3.7.4.1. [[GetOwnProperty]]

当调用命名属性对象O的[[GetOwnProperty]]内部方法,并传入属性键P时,执行以下步骤:

  1. AO接口

  2. objectO.[[Realm]]的全局对象

  3. 断言:object 实现A

  4. 如果使用属性名称P和对象object运行命名属性可见性算法的结果为true,则执行以下操作:

    1. operation为用于声明命名属性getter的操作。

    2. value为一个未初始化的变量。

    3. 如果operation是没有标识符定义的,则将value设置为执行接口描述中确定命名属性的值步骤的结果,属性名称为P

    4. 否则,operation是具有标识符的定义。将value设置为根据operation的描述,并将P作为唯一参数值执行步骤的结果。

    5. desc为一个新创建的属性描述符,且不包含任何字段。

    6. desc.[[Value]]设置为转换value为JavaScript值的结果。

    7. 如果A 实现了具有[LegacyUnenumerableNamedProperties] 扩展属性的接口,则将desc.[[Enumerable]]设置为false,否则将其设置为true

    8. desc.[[Writable]]设置为true,将desc.[[Configurable]]设置为true

    9. 返回desc

  5. 返回OrdinaryGetOwnProperty(O, P)。

3.7.4.2. [[DefineOwnProperty]]

当调用命名属性对象的[[DefineOwnProperty]]内部方法时,执行以下步骤:

  1. 返回false

3.7.4.3. [[Delete]]

当调用命名属性对象的[[Delete]]内部方法时,执行以下步骤:

  1. 返回false

3.7.4.4. [[SetPrototypeOf]]

当调用命名属性对象O的[[SetPrototypeOf]]内部方法,并传入JavaScript值V时,执行以下步骤:

  1. 如果O关联领域全局原型链可变性为true,返回? OrdinarySetPrototypeOf(O, V)。

  2. 返回? SetImmutablePrototype(O, V)。

3.7.4.5. [[PreventExtensions]]

当调用命名属性对象的[[PreventExtensions]]内部方法时,执行以下步骤:

  1. 返回false

注意:这使得命名属性对象通过使[[PreventExtensions]]失败而保持可扩展性。

3.7.5. 常量

常量暴露在接口对象旧版回调接口对象接口原型对象上,以及当接口使用 [Global] 扩展属性声明时,在实现该接口的单个对象上。

在给定领域 realm 的情况下,要在 target定义接口回调接口命名空间 definition 的常量,请执行以下步骤:
  1. 对于 definition 的每个作为成员常量 const

    1. 如果 const 未在 realm暴露,则继续

    2. value 为将 const 的 IDL 值转换为 JavaScript 值的结果。

    3. desc 为 PropertyDescriptor{[[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false, [[Value]]: value}。

    4. idconst标识符

    5. 执行 ! DefinePropertyOrThrow(target, id, desc)。

3.7.6. 属性

静态属性暴露在接口对象上。常规属性暴露在接口原型对象上,除非该属性是不可伪造的或接口是通过[Global] 扩展属性声明的,这种情况下,它们暴露在实现该接口的每个对象上。

定义常规属性,在接口命名空间定义上,目标为target,并且realm为给定的领域,运行以下步骤:
  1. attributes设为列表,包含作为定义常规属性成员

  2. 移除所有在不可伪造的接口上的属性

  3. 定义属性 attributes定义目标上,给定领域

定义静态属性,在接口命名空间定义上,目标为target,并且realm为给定的领域,运行以下步骤:
  1. attributes设为列表,包含作为定义静态属性成员

  2. 定义属性 attributes定义目标上,给定领域

定义不可伪造的常规属性,在接口命名空间定义上,目标为target,并且realm为给定的领域,运行以下步骤:
  1. attributes设为列表,包含作为定义不可伪造的 常规属性成员

  2. 定义属性 attributes定义目标上,给定领域

定义属性 attributes接口命名空间定义上,给定target领域 realm,执行以下步骤:
  1. 遍历 attributes中的每个属性 attr

    1. 如果attr未在realm暴露,则继续

    2. 创建attr属性getter,给定attr定义realm,并将结果赋值给getter

    3. 创建attr属性setter,给定attr定义realm,并将结果赋值给setter

      注意: 创建属性setter的算法会返回undefined,如果attr只读属性。

    4. 如果attr不可伪造的,则将configurable设为false,否则设为true

    5. desc设为PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}。

    6. id设为attr标识符

    7. 执行 ! DefinePropertyOrThrow(target, id, desc)。

    8. 如果attr的类型是带有类型参数T可观察数组类型,则将target可观察数组异类对象的后备设置为realm中创建的可观察数组异类对象的结果,给定Tattr设置索引值算法和attr删除索引值算法。

属性获取器按如下方式创建,给定一个属性 attribute、一个命名空间接口 target,以及一个领域 realm

  1. steps 为以下系列步骤:

    1. 尝试运行以下步骤:

      1. idlObject 为 null。

      2. 如果 target 是一个接口,并且 attribute 是一个常规属性

        1. jsValuethis 值(如果它不是 nullundefined),否则为 realm全局对象。 (如果全局对象未实现 target 且未指定 [LegacyLenientThis],这将在后续几个步骤中导致 TypeError。)

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValueattribute标识符和 "getter"。

        3. 如果 jsValue实现 target,则:

          1. 如果 attribute 使用 [LegacyLenientThis] 扩展属性指定,则返回 undefined

          2. 否则,抛出一个 TypeError

        4. 如果 attribute 的类型是可观察数组类型, 则返回 jsValue 针对 attribute支持的可观察数组奇异对象

        5. idlObject 设置为表示对 jsValue 引用的 IDL 接口类型值。

      3. R 为使用 idlObject 作为 this 运行 attribute获取器步骤的结果。

      4. 返回将 R 转换attribute 声明的类型的 JavaScript 值的结果。

    然后,如果抛出了异常 E

    1. 如果 attribute 的类型是promise 类型,则返回 ! Call(%Promise.reject%, %Promise%, «E»)。

    2. 否则,结束这些步骤并允许异常传播。

  2. FCreateBuiltinFunction(steps, « », realm)。

  3. name 为在 attribute标识符前加上字符串 "get " 的结果。

  4. 执行 SetFunctionName(F, name)。

  5. 执行 SetFunctionLength(F, 0)。

  6. 返回 F

属性设置器按如下方式创建,给定一个属性 attribute、一个命名空间接口 target,以及一个领域 realm

  1. 如果 target 是一个命名空间

    1. 断言:attribute只读的

    2. 返回 undefined

  2. 如果 attribute只读的并且没有 [LegacyLenientSetter]、 [PutForwards] 或 [Replaceable] 扩展属性,则返回 undefined;不存在属性设置器函数。

  3. 断言:attribute 的类型不是promise 类型

  4. steps 为以下系列步骤:

    1. 如果没有传递参数,则抛出一个 TypeError

    2. V 为传递的第一个参数的值。

    3. idattribute标识符

    4. idlObject 为 null。

    5. 如果 attribute 是一个常规属性

      1. jsValuethis 值(如果它不是 nullundefined),否则为 realm全局对象。 (如果全局对象未实现 target 且未指定 [LegacyLenientThis],这将在后续几个步骤中导致 TypeError。)

      2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValueid 和 "setter"。

      3. 如果 jsValue 实现target,则令 validThis 为 true,否则为 false。

      4. 如果 validThis 为 false 且 attribute 未使用 [LegacyLenientThis] 扩展属性指定,则抛出一个 TypeError

      5. 如果 attribute 使用 [Replaceable] 扩展属性声明,则:

        1. 执行 ? CreateDataPropertyOrThrow(jsValue, id, V)。

        2. 返回 undefined

      6. 如果 validThis 为 false,则返回 undefined

      7. 如果 attribute 使用 [LegacyLenientSetter] 扩展属性声明,则 返回 undefined

      8. 如果 attribute 使用 [PutForwards] 扩展属性声明,则:

        1. Q? Get(jsValue, id)。

        2. 如果 Q 不是一个对象,则抛出一个 TypeError

        3. forwardId 为 [PutForwards] 扩展属性的标识符参数。

        4. 执行 ? Set(Q, forwardId, V, false)。

        5. 返回 undefined

      9. idlObject 设置为表示对 jsValue 引用的 IDL 接口类型值。

      10. 如果 attribute 的类型是类型参数为 T可观察数组类型

        1. newValues 为将 V 转换为类型为 sequence<T> 的 IDL 值的结果。

        2. oaidlObjectattribute支持的可观察数组奇异对象

        3. 设置 oa.[[ProxyHandler]] 的长度为 0。

        4. i 为 0。

        5. i < newValues大小时:

          1. 给定 newValues[i] 和 i,执行 oa.[[ProxyHandler]].[[SetAlgorithm]] 给出的算法步骤。

          2. 追加 newValues[i] 到 oa.[[ProxyHandler]].[[BackingList]]。

        6. 返回 undefined

    6. idlValue 按如下方式确定:

      attribute 的类型是枚举
      1. S? ToString(V)。

      2. 如果 S 不是枚举的值之一,则返回 undefined

      3. 否则,idlValue 是等于 S 的枚举值。

      否则
      idlValue 是将 V 转换attribute 类型的 IDL 值的结果。
    7. 使用 idlObject 作为 this 并使用 idlValue 作为给定值,执行 attribute设置器步骤

    8. 返回 undefined

  5. FCreateBuiltinFunction(steps, « », realm)。

  6. name 为在 id 前加上字符串 "set " 的结果。

  7. 执行 SetFunctionName(F, name)。

  8. 执行 SetFunctionLength(F, 1)。

  9. 返回 F

注意: 尽管IDL属性只有一个属性,但由于访问器属性的getter和setter传递了this值,用于访问与IDL属性对应的对象上的属性,因此它们能够暴露特定实例的数据。

注意: 尝试给一个对应于只读属性赋值时,其行为会根据脚本是否处于严格模式而有所不同。在严格模式下,这样的赋值将导致抛出TypeError错误。而在非严格模式下,赋值操作将被忽略。

3.7.7. 操作

对于接口上定义的每个暴露的操作的唯一标识符, 都存在一个对应的属性。静态操作接口对象上公开。常规操作接口原型对象上公开, 除非该操作是不可伪造的或者 接口是用 [Global] 扩展属性声明的, 在这种情况下,它们在每个实现该接口的对象上公开。

在给定领域 realm 的情况下,要在 target定义接口命名空间 definition 的常规操作, 请执行以下步骤:
  1. operations 为作为 definition 成员常规操作列表

  2. operations移除所有不可伪造的操作

  3. 给定 realm,在 target定义 definition操作

在给定领域 realm 的情况下,要在 target定义接口命名空间 definition 的静态操作, 请执行以下步骤:
  1. operations 为作为 definition 成员静态操作列表

  2. 给定 realm,在 target定义 definition操作

在给定领域 realm 的情况下,要在 target定义接口命名空间 definition 的不可伪造的常规操作, 请执行以下步骤:
  1. operations 为作为 definition 成员不可伪造的常规操作列表

  2. 给定 realm,在 target定义 definition操作

在给定领域 realm 的情况下,要在 target定义接口命名空间 definition操作,请执行以下步骤:
  1. 对于 operations 中的每个操作 op

    1. 如果 op 未在 realm暴露,则继续

    2. method 为给定 opdefinitionrealm 创建操作函数的结果。

    3. 如果 op不可伪造的,则令 modifiablefalse,否则为 true

    4. desc 为 PropertyDescriptor{[[Value]]: method, [[Writable]]: modifiable, [[Enumerable]]: true, [[Configurable]]: modifiable}。

    5. idop标识符

    6. 执行 ! DefinePropertyOrThrow(target, id, desc)。

给定一个操作 op、一个命名空间接口 target, 以及一个领域 realm创建操作函数
  1. idop标识符

  2. steps 为给定函数参数值 args 的以下系列步骤:

    1. 尝试运行以下步骤:

      1. idlObject 为 null。

      2. 如果 target 是一个接口,并且 op 不是一个静态操作

        1. jsValuethis 值(如果它不是 nullundefined),否则为 realm全局对象。 (如果全局对象未实现 target 且未指定 [LegacyLenientThis],这将在后续几个步骤中导致 TypeError。)

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValueid 和 "method"。

        3. 如果 jsValue实现接口 target,则抛出一个 TypeError

        4. idlObject 设置为表示对 jsValue 引用的 IDL 接口类型值。

      3. nargs大小

      4. 针对常规操作(如果 op 是常规操作)或静态操作(如果 op 是静态操作), 在 target 上使用标识符 id 和参数计数 n计算有效重载集,并令 S 为结果。

      5. 令 <operation, values> 为将 Sargs 传递给重载解析算法的结果。

      6. Rnull

      7. 如果 operation 使用 [Default] 扩展属性声明,则:

        1. 断言:operation 具有默认方法步骤

        2. R 为使用 idlObject 作为 this 并使用 values 作为参数值运行 operation默认方法步骤的结果。

      8. 否则,令 R 为使用 idlObject 作为 this 并使用 values 作为参数值运行 operation方法步骤的结果。

      9. 返回 R转换为 JavaScript 值

        假设 Rop 声明要返回的类型的 IDL 值。[whatwg/webidl 问题 #674]

    然后,如果抛出了异常 E

    1. 如果 op返回类型promise 类型,则返回 ! Call(%Promise.reject%, %Promise%, «E»)。

    2. 否则,结束这些步骤并允许异常传播。

  3. FCreateBuiltinFunction(steps, « », realm)。

  4. 执行 SetFunctionName(F, id)。

  5. 针对常规操作(如果 op 是常规操作)或静态操作(如果 op 是静态操作), 在 target 上使用标识符 id 和参数计数 0,计算有效重载集,并令 S 为结果。

  6. lengthS 中条目中最短参数列表的长度。

  7. 执行 SetFunctionLength(F, length)。

  8. 返回 F

3.7.7.1. 默认操作

如果常规操作具有默认方法步骤,其标识符会出现在下表的第一列中。在这种情况下,其默认方法步骤由第二列链接的算法给出,操作必须具有表中第三列给出的返回类型。

标识符 默认方法步骤 返回类型
"toJSON" 默认 toJSON 步骤 object

没有默认方法步骤常规操作不得与 [Default] 扩展属性 一起声明。

3.7.7.1.1. 默认 toJSON 操作

接口 I默认 toJSON 步骤如下:

  1. map 为一个新的有序映射

  2. stack 为为接口 I 创建继承堆栈的结果。

  3. 给定thisstackmap,调用收集继承堆栈的属性值

  4. resultOrdinaryObjectCreate(%Object.prototype%)。

  5. 对于 map 中的每个 keyvalue

    1. k转换为 JavaScript 值key

    2. v转换为 JavaScript 值value

    3. 执行 ! CreateDataPropertyOrThrow(result, k, v)。

  6. 返回 result

给定一个平台对象 object、一个堆栈 stack 和一个有序映射 map收集继承堆栈的属性值

  1. I 为从 stack 弹出的结果。

  2. 给定 objectImap,调用收集属性值

  3. 如果 stack 不为空,则给定 objectstackmap,调用收集继承堆栈的属性值

给定一个平台对象 object、一个接口 I 和一个有序映射 map收集属性值

  1. 如果在 I 上声明了带有 [Default] 扩展属性toJSON 操作,则对于 I 的每个作为接口成员暴露的常规属性 attr,按顺序执行:

    1. idattr标识符

    2. value 为以 object 作为this 运行 attr获取器步骤的结果。

    3. 如果 value 是一个JSON 类型,则设置 map[id] 为 value

接口 I 创建继承堆栈, 请执行以下步骤:

  1. stack 为一个新的堆栈

  2. I 推入 stack

  3. I 从一个接口继承时,

    1. I 为该接口

    2. I 推入 stack

  4. 返回 stack

只有声明了带有 toJSON 操作的 [Default] 扩展属性接口常规属性才会被包含,即使某个继承接口声明了这样的 toJSON 操作。例如,考虑以下IDL 片段

[Exposed=Window]
  interface A {
    [Default] object toJSON();
    attribute DOMString a;
  };
  
  [Exposed=Window]
  interface B : A {
    attribute DOMString b;
  };
  
  [Exposed=Window]
  interface C : B {
    [Default] object toJSON();
    attribute DOMString c;
  };
  

调用上面定义的实现接口 C 的对象的 toJSON() 方法将返回以下 JSON 对象:

{
  "a": "...",
  "c": "..."
}

调用上面定义的实现接口 A(或 B)的对象的 toJSON() 方法将返回:

{
  "a": "..."
}

还可以在接口混入(或部分接口)上声明 toJSON 操作,其效果等同于在原始接口上声明。例如,考虑以下IDL 片段

[Exposed=Window]
  interface D {
    attribute DOMString d;
  };
  
  interface mixin M {
    [Default] object toJSON();
    attribute DOMString m;
  };
  
  D includes M;
  

调用上面定义的实现接口 D 的对象的 toJSON() 方法将返回:

{
  "d":"...",
  "m":"..."
}

3.7.8. 字符串化器

如果接口有一个暴露的字符串化器, 那么必须存在一个具有以下特征的属性:

3.7.9. 可迭代声明

要在给定领域 realm 的情况下,在 target定义接口 definition 的迭代方法,请执行以下步骤:
  1. 如果 definition 具有索引属性获取器,则:

    1. 执行 DefineMethodProperty(target, %Symbol.iterator%, %Array.prototype.values%, false)。

    2. 如果 definition 具有值迭代器,则:

      1. 执行 ! CreateDataPropertyOrThrow(target, "entries", %Array.prototype.entries%)。

      2. 执行 ! CreateDataPropertyOrThrow(target, "keys", %Array.prototype.keys%)。

      3. 执行 ! CreateDataPropertyOrThrow(target, "values", %Array.prototype.values%)。

      4. 执行 ! CreateDataPropertyOrThrow(target, "forEach", %Array.prototype.forEach%)。

  2. 否则,如果 definition 具有对迭代器,则:

    1. 定义 %Symbol.iterator%entries 方法:

      1. steps 为以下系列步骤:

        1. jsValue? ToObject(this 值)。

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“%Symbol.iterator%”和“method”。

        3. 如果 jsValue实现 definition,则抛出 TypeError

        4. definition 返回一个新创建的默认迭代器对象,其目标jsValue种类为“key+value”,且索引设置为 0。

      2. FCreateBuiltinFunction(steps, « », realm)。

      3. 执行 SetFunctionName(F, "entries")。

      4. 执行 SetFunctionLength(F, 0)。

      5. 执行 DefineMethodProperty(target, %Symbol.iterator%, F, false)。

      6. 执行 ! CreateDataPropertyOrThrow(target, "entries", F)。

    2. 定义 keys 方法:

      1. steps 为以下系列步骤:

        1. jsValue? ToObject(this 值)。

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“keys”和“method”。

        3. 如果 jsValue实现 definition,则抛出 TypeError

        4. definition 返回一个新创建的默认迭代器对象,其目标jsValue种类为“key”,且索引设置为 0。

      2. FCreateBuiltinFunction(steps, « », realm)。

      3. 执行 SetFunctionName(F, "keys")。

      4. 执行 SetFunctionLength(F, 0)。

      5. 执行 ! CreateDataPropertyOrThrow(target, "keys", F)。

    3. 定义 values 方法:

      1. steps 为以下系列步骤:

        1. jsValue? ToObject(this 值)。

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“values”和“method”。

        3. 如果 jsValue实现 definition,则抛出 TypeError

        4. definition 返回一个新创建的默认迭代器对象,其目标jsValue种类为“value”,且索引设置为 0。

      2. FCreateBuiltinFunction(steps, « », realm)。

      3. 执行 SetFunctionName(F, "values")。

      4. 执行 SetFunctionLength(F, 0)。

      5. 执行 ! CreateDataPropertyOrThrow(target, "values", F)。

    4. 定义 forEach 方法:

      1. steps 为给定函数参数值 callbackthisArg 的以下系列步骤:

        1. jsValue? ToObject(this 值)。

        2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“forEach”和“method”。

        3. 如果 jsValue实现 definition,则抛出 TypeError

        4. idlCallbackcallback转换为 Function

        5. idlObject 为表示对 jsValue 引用的 IDL 接口类型值。

        6. pairsidlObject要迭代的值对列表。

        7. i 为 0。

        8. i < pairs大小时:

          1. pairpairs[i]。

          2. 使用 « pair, pair, idlObject » 调用 idlCallback,并以 thisArg 作为回调 this 值

          3. pairs 设置为 idlObject 当前的要迭代的值对列表。(它可能已更改。)

          4. i 设置为 i + 1。

      2. FCreateBuiltinFunction(steps, « », realm)。

      3. 执行 SetFunctionName(F, "forEach")。

      4. 执行 SetFunctionLength(F, 1)。

      5. 执行 ! CreateDataPropertyOrThrow(target, "forEach", F)。

3.7.9.1. 默认迭代器对象

给定接口、目标和迭代种类的默认迭代器对象是一个对象,其 [[Prototype]] 内部插槽是该接口迭代器原型对象

默认迭代器对象具有三个内部值:

注意: 默认迭代器对象仅用于对迭代器值迭代器,由于它们目前仅限于迭代对象的支持的索引属性, 因此使用标准的 JavaScript 数组迭代器对象。

注意: 默认迭代器对象没有类字符串;当在给定接口默认迭代器对象上调用 Object.prototype.toString() 时,将使用该接口迭代器原型对象类字符串

3.7.9.2. 迭代器原型对象

给定接口迭代器原型对象是一个对象,它存在于每个具有对迭代器的接口中。它充当该接口的默认迭代器对象的原型。

迭代器原型对象的 [[Prototype]] 内部插槽必须是 %Iterator.prototype%

值对 pair 和种类 kind迭代器结果由以下步骤给出:
  1. result 为由 kind 的值确定的值:

    "key"
    1. idlKeypair

    2. key 为将 idlKey 转换为 JavaScript 值的结果。

    3. resultkey

    "value"
    1. idlValuepair

    2. value 为将 idlValue 转换为 JavaScript 值的结果。

    3. resultvalue

    "key+value"
    1. idlKeypair

    2. idlValuepair

    3. key 为将 idlKey 转换为 JavaScript 值的结果。

    4. value 为将 idlValue 转换为 JavaScript 值的结果。

    5. array! ArrayCreate(2)。

    6. 执行 ! CreateDataPropertyOrThrow(array, "0", key)。

    7. 执行 ! CreateDataPropertyOrThrow(array, "1", value)。

    8. resultarray

  2. 返回 CreateIteratorResultObject(result, false)。

迭代器原型对象必须具有一个 next 数据属性,其属性为 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }, 其值是一个内置函数对象,其行为如下:

  1. interface迭代器原型对象所在的接口

  2. thisValuethis 值。

  3. object? ToObject(thisValue)。

  4. 如果 object 是一个平台对象, 则执行安全检查,传递:

    • 平台对象 object

    • 标识符 "next",以及

    • 类型 "method"。

  5. 如果 object 不是 interface默认迭代器对象, 则抛出一个 TypeError

  6. indexobject索引

  7. kindobject种类

  8. valuesobject目标要迭代的值对

  9. lenvalues 的长度。

  10. 如果 index 大于或等于 len,则返回 CreateIteratorResultObject(undefined, true)。

  11. pairvalues 中索引为 index 的条目。

  12. object 的索引设置为 index + 1。

  13. 返回 pairkind迭代器结果

给定接口迭代器原型对象类字符串是连接接口标识符和字符串 " Iterator" 的结果。

3.7.10. 异步可迭代声明

要在给定领域 realm 的情况下,在 target定义接口 definition 的异步迭代方法,请执行以下步骤:
  1. 如果 definition 没有异步可迭代声明(任何一种),则返回。

  2. 断言:definition 没有索引属性获取器可迭代声明

  3. 如果 definition 具有成对异步可迭代声明,则定义 %Symbol.asyncIterator%entries 方法:

    1. steps 为给定函数参数值 args 的以下系列步骤:

      1. jsValue? ToObject(this 值)。

      2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“%Symbol.asyncIterator%”和“method”。

      3. 如果 jsValue实现 definition,则抛出 TypeError

      4. idlObject 为表示对 jsValue 引用的 IDL 接口类型值。

      5. idlArgs 为给定 args 转换异步迭代器方法参数的结果。

      6. definition 创建一个新的默认异步迭代器对象 iterator,其目标idlObject种类为“key+value”,且已完成设置为 false。

      7. 如果存在此类步骤,则使用 idlObjectiteratoridlArgsdefinition 运行异步迭代器初始化步骤

      8. 返回 iterator

    2. FCreateBuiltinFunction(steps, « », realm)。

    3. 执行 SetFunctionName(F, "entries")。

    4. 执行 SetFunctionLength(F, 0)。

    5. 执行 DefineMethodProperty(target, %Symbol.asyncIterator%, F, false)。

    6. 执行 ! CreateDataPropertyOrThrow(target, "entries", F)。

  4. 如果 definition 具有成对异步可迭代声明,则定义 keys 方法:

    1. steps 为给定函数参数值 args 的以下系列步骤:

      1. jsValue? ToObject(this 值)。

      2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“keys”和“method”。

      3. 如果 jsValue实现 definition,则抛出 TypeError

      4. idlObject 为表示对 jsValue 引用的 IDL 接口类型值。

      5. idlArgs 为给定 args 转换异步迭代器方法参数的结果。

      6. definition 创建一个新的默认异步迭代器对象 iterator,其目标idlObject种类为“key”,且已完成设置为 false。

      7. 如果存在此类步骤,则使用 idlObjectiteratoridlArgsdefinition 运行异步迭代器初始化步骤

      8. 返回 iterator

    2. FCreateBuiltinFunction(steps, « », realm)。

    3. 执行 SetFunctionName(F, "keys")。

    4. 执行 SetFunctionLength(F, 0)。

    5. 执行 ! CreateDataPropertyOrThrow(target, "keys", F)。

  5. 定义 values 方法,可能还定义 %Symbol.asyncIterator% 方法:

    1. steps 为给定函数参数值 args 的以下系列步骤:

      1. jsValue? ToObject(this 值)。

      2. 如果 jsValue 是一个平台对象,则执行安全检查, 传递 jsValue、“values”和“method”。

      3. 如果 jsValue实现 definition,则抛出 TypeError

      4. idlObject 为表示对 jsValue 引用的 IDL 接口类型值。

      5. idlArgs 为给定 args 转换异步迭代器方法参数的结果。

      6. definition 创建一个新的默认异步迭代器对象 iterator,其目标idlObject种类为“value”,且已完成设置为 false。

      7. 如果存在此类步骤,则使用 idlObjectiteratoridlArgsdefinition 运行异步迭代器初始化步骤

      8. 返回 iterator

    2. FCreateBuiltinFunction(steps, « », realm)。

    3. 执行 SetFunctionName(F, "values")。

    4. 执行 SetFunctionLength(F, 0)。

    5. 执行 ! CreateDataPropertyOrThrow(target, "values", F)。

    6. 如果 definition 具有值异步可迭代声明,则执行 ! DefineMethodProperty(target, %Symbol.asyncIterator%, F, false)。

转换异步迭代器方法的参数, 给定一个具有异步可迭代声明接口 definition 和一个 JavaScript 值列表 args
  1. idlArgs 为一个空列表。

  2. argCountdefinition异步可迭代声明的参数数量,如果异步可迭代声明没有参数列表,则为 0。

  3. i 为 0。

  4. i < argCount 时:

    1. 如果 iargs大小, 或者如果 args[i] 是 undefined, 则:

      1. 如果索引 i 处的异步可迭代声明的参数是用默认值声明的,则将该默认值附加idlArgs

      2. 否则,将特殊值“missing”附加idlArgs

    2. 否则,将转换 args[i] 为异步可迭代声明的参数列表中索引 i 处给出的 IDL 类型的结果附加idlArgs

    3. i 设置为 i + 1。

  5. 返回 idlArgs

这本质上是针对不允许重载且所有参数都是可选的情况的重载解析算法的超特殊化。

3.7.10.1. 默认异步迭代器对象

给定接口、目标和迭代种类的默认异步迭代器对象是一个对象,其 [[Prototype]] 内部插槽是该接口异步迭代器原型对象

默认异步迭代器对象具有内部值:

注意: 默认异步迭代器对象没有类字符串;当在给定接口默认异步迭代器对象上调用 Object.prototype.toString() 时,将使用该接口异步迭代器原型对象类字符串

3.7.10.2. 异步迭代器原型对象

给定接口异步迭代器原型对象是一个对象,它存在于每个具有异步可迭代声明的接口中。 它充当该接口的默认异步迭代器对象的原型。

异步迭代器原型对象的 [[Prototype]] 内部插槽必须是 %AsyncIteratorPrototype%

异步迭代器原型对象必须具有一个 next 数据属性,其属性为 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }, 其值是一个内置函数对象,其行为如下:

  1. interface异步迭代器原型对象所在的接口

  2. thisValidationPromiseCapability! NewPromiseCapability(%Promise%)。

  3. thisValuethis 值。

  4. objectCompletion(ToObject(thisValue))。

  5. IfAbruptRejectPromise(object, thisValidationPromiseCapability)。

  6. 如果 object 是一个平台对象,则执行安全检查,传递:

    • 平台对象 object

    • 标识符 "next",以及

    • 类型 "method"。

    如果这抛出了一个异常 e,则:

    1. 执行 ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « e »)。

    2. 返回 thisValidationPromiseCapability.[[Promise]]。

  7. 如果 object 不是 interface默认异步迭代器对象,则:

    1. error 为一个新的 TypeError

    2. 执行 ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « error »)。

    3. 返回 thisValidationPromiseCapability.[[Promise]]。

  8. nextSteps 为以下步骤:

    1. nextPromiseCapability! NewPromiseCapability(%Promise%)。

    2. 如果 object已完成为 true,则:

      1. resultCreateIteratorResultObject(undefined, true)。

      2. 执行 ! Call(nextPromiseCapability.[[Resolve]], undefined, « result »)。

      3. 返回 nextPromiseCapability.[[Promise]]。

    3. kindobject种类

    4. nextPromise 为使用 object目标object 获取下一个迭代结果的结果。

    5. fulfillSteps 为给定 next 的以下步骤:

      1. object进行中的 Promise设置为 null。

      2. 如果 next迭代结束,则:

        1. object已完成设置为 true。

        2. 返回 CreateIteratorResultObject(undefined, true)。

      3. 否则,如果 interface 具有成对异步可迭代声明

        1. 断言:next 是一个值对

        2. 返回 nextkind迭代器结果

      4. 否则:

        1. 断言:interface 具有值异步可迭代声明

        2. 断言:next 是声明中出现的类型的值。

        3. valuenext转换为 JavaScript 值

        4. 返回 CreateIteratorResultObject(value, false)。

    6. onFulfilledCreateBuiltinFunction(fulfillSteps, « »)。

    7. rejectSteps 为给定 reason 的以下步骤:

      1. object进行中的 Promise设置为 null。

      2. object已完成设置为 true。

      3. 抛出 reason

    8. onRejectedCreateBuiltinFunction(rejectSteps, « »)。

    9. 执行 PerformPromiseThen(nextPromise, onFulfilled, onRejected, nextPromiseCapability)。

    10. 返回 nextPromiseCapability.[[Promise]]。

  9. ongoingPromiseobject进行中的 Promise

  10. 如果 ongoingPromise 不为 null,则:

    1. afterOngoingPromiseCapability! NewPromiseCapability(%Promise%)。

    2. onSettledCreateBuiltinFunction(nextSteps, « »)。

    3. 执行 PerformPromiseThen(ongoingPromise, onSettled, onSettled, afterOngoingPromiseCapability)。

    4. object进行中的 Promise设置为 afterOngoingPromiseCapability.[[Promise]]。

  11. 否则:

    1. object进行中的 Promise设置为运行 nextSteps 的结果。

  12. 返回 object进行中的 Promise

如果为接口定义了异步迭代器返回算法,则异步迭代器原型对象必须具有一个 return 数据属性,其属性为 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }, 其值是一个内置函数对象,接受一个参数 value,其行为如下:

  1. interface异步迭代器原型对象所在的接口

  2. returnPromiseCapability! NewPromiseCapability(%Promise%)。

  3. thisValuethis 值。

  4. objectCompletion(ToObject(thisValue))。

  5. IfAbruptRejectPromise(object, returnPromiseCapability)。

  6. 如果 object 是一个平台对象,则执行安全检查,传递:

    • 平台对象 object

    • 标识符 "return",以及

    • 类型 "method"。

    如果这抛出了一个异常 e,则:

    1. 执行 ! Call(returnPromiseCapability.[[Reject]], undefined, « e »)。

    2. 返回 returnPromiseCapability.[[Promise]]。

  7. 如果 object 不是 interface默认异步迭代器对象,则:

    1. error 为一个新的 TypeError

    2. 执行 ! Call(returnPromiseCapability.[[Reject]], undefined, « error »)。

    3. 返回 returnPromiseCapability.[[Promise]]。

  8. returnSteps 为以下步骤:

    1. returnPromiseCapability! NewPromiseCapability(%Promise%)。

    2. 如果 object已完成为 true,则:

      1. resultCreateIteratorResultObject(value, true)。

      2. 执行 ! Call(returnPromiseCapability.[[Resolve]], undefined, « result »)。

      3. 返回 returnPromiseCapability.[[Promise]]。

    3. object已完成设置为 true。

    4. 返回运行异步迭代器返回算法的结果,给定 object目标objectvalue

  9. ongoingPromiseobject进行中的 Promise

  10. 如果 ongoingPromise 不为 null,则:

    1. afterOngoingPromiseCapability! NewPromiseCapability(%Promise%)。

    2. onSettledCreateBuiltinFunction(returnSteps, « »)。

    3. 执行 PerformPromiseThen(ongoingPromise, onSettled, onSettled, afterOngoingPromiseCapability)。

    4. object进行中的 Promise设置为 afterOngoingPromiseCapability.[[Promise]]。

  11. 否则:

    1. object进行中的 Promise设置为运行 returnSteps 的结果。

  12. fulfillSteps 为以下步骤:

    1. 返回 CreateIteratorResultObject(value, true)。

  13. onFulfilledCreateBuiltinFunction(fulfillSteps, « »)。

  14. 执行 PerformPromiseThen(object进行中的 Promise, onFulfilled, undefined, returnPromiseCapability)。

  15. 返回 returnPromiseCapability.[[Promise]]。

给定接口异步迭代器原型对象类字符串是连接接口标识符和字符串 " AsyncIterator" 的结果。

3.7.11. 类 Map 声明

如果接口 A 使用类 Map 声明进行声明, 那么在 A接口原型对象上存在许多附加属性。 这些附加属性将在下面的小节中描述。

3.7.11.1. size

A接口原型对象上必须存在一个 size 属性,具有以下特征:

3.7.11.2. %Symbol.iterator%

A接口原型对象上必须存在一个数据属性,其名称为 %Symbol.iterator% 符号,属性为 { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }, 其值是作为 entries 属性值的函数对象

要从map map 和一个种类 kind("key+value"、"key" 或 "value")创建一个 map 迭代器
  1. closure 为一个新的抽象闭包,它没有参数,捕获 mapkind,并在调用时执行以下步骤:

    1. 对于 map 中的每个 keyvalue

      1. keyvalue 设置为每个转换为 JavaScript 值的结果。

      2. 如果 kind 是 "key",则令 resultkey

      3. 否则,如果 kind 是 "value",则令 resultvalue

      4. 否则,令 resultCreateArrayFromListkey, value »)。

      5. 执行 ? GeneratorYield(CreateIteratorResultObject(result, false))。

      注意: map大小及其条目的顺序, 可能在此抽象操作的执行因 Yield 而暂停时已更改。

    2. 返回 undefined

  2. 返回 CreateIteratorFromClosure(closure, "%MapIteratorPrototype%", %MapIteratorPrototype%)。

3.7.11.3. entries

A接口原型对象上必须存在一个 entries 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "entries"。

3.7.11.4. keys

A接口原型对象上必须存在一个 keys 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "keys"。

3.7.11.5. values

A接口原型对象上必须存在一个 values 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "values"。

3.7.11.6. forEach

A接口原型对象上必须存在一个 forEach 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "forEach"。

3.7.11.7. get

A接口原型对象上必须存在一个 get 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "get"。

3.7.11.8. has

A接口原型对象上必须存在一个 has 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "has"。

3.7.11.9. set

如果 A 没有声明标识符为 "set" 的成员, 并且 A 是使用读写类 map 声明进行声明的, 那么在 A接口原型对象上必须存在一个 set 数据属性,具有以下特征:

函数对象length 属性的值是数字值 2

函数对象name 属性的值是字符串值 "set"。

如果接口确实声明了一个 set 方法, 它应该类似地将 -0 键映射到 +0, 并且必须返回this

3.7.11.10. delete

如果 A 没有声明标识符为 "delete" 的成员, 并且 A 是使用读写类 map 声明进行声明的, 那么在 A接口原型对象上必须存在一个 delete 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "delete"。

如果接口确实声明了一个 delete 方法, 它应该类似地将 -0 键映射到 +0, 并且必须返回一个布尔值, 指示该键是否存在。

3.7.11.11. clear

如果 A 没有声明标识符为 "clear" 的成员, 并且 A 是使用读写类 map 声明进行声明的, 那么在 A接口原型对象上必须存在一个 clear 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "clear"。

如果接口确实声明了一个 clear 方法, 它必须保留map 条目对象(而不是生成一个新的) 并且必须返回 undefined

3.7.12. 类 Set 声明

如果接口 A 使用类 Set 声明进行声明, 那么在 A接口原型对象上存在许多附加属性。 这些附加属性将在下面的小节中描述。

3.7.12.1. size

A接口原型对象上必须存在一个 size 属性,具有以下特征:

3.7.12.2. %Symbol.iterator%

A接口原型对象上必须存在一个数据属性,其名称为 %Symbol.iterator% 符号,属性为 { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }, 其值是作为 values 属性值的函数对象

要从set set 和一个种类 kind("key+value" 或 "value")创建一个 set 迭代器
  1. closure 为一个新的抽象闭包,它没有参数,捕获 setkind,并在调用时执行以下步骤:

    1. 对于 set 中的每个 entry

      1. entry 设置为 entry 转换为 JavaScript 值的结果。

      2. 如果 kind 是 "value",则令 resultentry

      3. 否则,令 resultCreateArrayFromListentry, entry »)。

      4. 执行 ? GeneratorYield(CreateIteratorResultObject(result, false))。

      注意: set大小及其条目的顺序, 可能在此抽象操作的执行因 Yield 而暂停时已更改。

    2. 返回 undefined

  2. 返回 CreateIteratorFromClosure(closure, "%SetIteratorPrototype%", %SetIteratorPrototype%)。

3.7.12.3. entries

A接口原型对象上必须存在一个 entries 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "entries"。

3.7.12.4. keys

A接口原型对象上必须存在一个 keys 数据属性,属性为 { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }, 其值是作为 values 属性值的函数对象

3.7.12.5. values

A接口原型对象上必须存在一个 values 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "values"。

3.7.12.6. forEach

A接口原型对象上必须存在一个 forEach 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "forEach"。

3.7.12.7. has

A接口原型对象上必须存在一个 has 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "has"。

3.7.12.8. add

如果 A 没有声明标识符为 "add" 的成员, 并且 A 是使用读写类 set 声明进行声明的, 那么在 A接口原型对象上必须存在一个 add 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "add"。

如果接口确实声明了一个 add 方法, 它应该类似地将 -0 值映射到 +0, 并且必须返回已设置的值。

3.7.12.9. delete

如果 A 没有声明标识符为 "delete" 的成员, 并且 A 是使用读写类 set 声明进行声明的, 那么在 A接口原型对象上必须存在一个 delete 数据属性,具有以下特征:

函数对象length 属性的值是数字值 1

函数对象name 属性的值是字符串值 "delete"。

如果接口确实声明了一个 delete 方法, 它应该类似地将 -0 值映射到 +0, 并且必须返回一个布尔值, 指示该值是否存在。

3.7.12.10. clear

如果 A 没有声明标识符为 "clear" 的成员, 并且 A 是使用读写类 set 声明进行声明的, 那么在 A接口原型对象上必须存在一个 clear 数据属性,具有以下特征:

函数对象length 属性的值是数字值 0

函数对象name 属性的值是字符串值 "clear"。

如果接口确实声明了一个 clear 方法, 它必须保留set 条目对象(而不是生成一个新的) 并且必须返回 undefined

3.8. 实现接口的平台对象

如果 JavaScript 值 value 是一个对象并且 value 有一个 [[PrimaryInterface]] 内部插槽,则该 JavaScript 值 value 是一个平台对象
如果 JavaScript 值 value 是一个平台对象并且 value.[[PrimaryInterface]] 的包含继承接口包含 interface,则该 JavaScript 值 value 实现一个接口 interface

规范可能会以各种方式引用概念“object 实现 interface”,包括“object 是一个 interface 对象”。

每个平台对象都与一个领域相关联,就像初始对象一样。 此领域存储在平台对象的 [[Realm]] 插槽中。 使用 Web IDL 的规范有责任说明每个平台对象与哪个领域(或者,通过代理,哪个全局对象)相关联。 特别是,下面的算法将新的平台对象与作为参数给出的领域相关联。

创建一个实现接口 interface 的新对象,并使用领域 realm,请执行以下步骤:
  1. 返回内部创建实现 interface 的新对象的结果,使用 realmundefined

内部创建实现接口 interface 的新对象,并使用领域 realm 和 JavaScript 值 newTarget,请执行以下步骤:
  1. 断言:interfacerealm公开

  2. 如果 newTargetundefined,则:

    1. prototyperealminterface接口原型对象

  3. 否则:

    1. 断言:IsCallable(newTarget) 为 true。

    2. prototype? Get(newTarget, "prototype")。

    3. 如果 prototype 不是对象,则:

      1. targetRealm? GetFunctionRealm(newTarget)。

      2. prototype 设置为 targetRealminterface接口原型对象

  4. slots 为 « [[Prototype]], [[Extensible]], [[Realm]], [[PrimaryInterface]] »。

  5. 如果 interfaceDOMException,则将 [[ErrorData]] 附加到 slots

  6. instanceMakeBasicObject(slots)。

  7. instance.[[Realm]] 设置为 realm

  8. instance.[[PrimaryInterface]] 设置为 interface

  9. instance.[[Prototype]] 设置为 prototype

  10. interfacesinterface包含继承接口

  11. 对于 interfaces 中的每个接口 ancestor interface

    1. unforgeablesrealmancestor interface接口对象的 [[Unforgeables]] 插槽的值。

    2. keys! unforgeables.[[OwnPropertyKeys]]()。

    3. 对于 keys 中的每个元素 key

      1. descriptor! unforgeables.[[GetOwnProperty]](key)。

      2. 执行 ! DefinePropertyOrThrow(instance, key, descriptor)。

  12. 如果 interface 使用 [Global] 扩展属性声明,则:

    1. instance定义 interface 的常规操作,给定 realm

    2. instance定义 interface 的常规属性,给定 realm

    3. instance定义 interface 的迭代方法,给定 realm

    4. instance定义 interface 的异步迭代方法,给定 realm

    5. instance定义全局属性引用,给定 realm

    6. instance.[[SetPrototypeOf]] 设置为 § 3.8.1 [[SetPrototypeOf]] 中定义的。

  13. 否则,如果 interfaces 包含一个接口,该接口支持索引属性命名属性或两者都支持:

    1. instance.[[GetOwnProperty]] 设置为 § 3.9.1 [[GetOwnProperty]] 中定义的。

    2. instance.[[Set]] 设置为 § 3.9.2 [[Set]] 中定义的。

    3. instance.[[DefineOwnProperty]] 设置为 § 3.9.3 [[DefineOwnProperty]] 中定义的。

    4. instance.[[Delete]] 设置为 § 3.9.4 [[Delete]] 中定义的。

    5. instance.[[PreventExtensions]] 设置为 § 3.9.5 [[PreventExtensions]] 中定义的。

    6. instance.[[OwnPropertyKeys]] 设置为 § 3.9.6 [[OwnPropertyKeys]] 中定义的。

  14. 返回 instance

target 上定义全局属性引用,给定领域 realm, 请执行以下步骤:
  1. interfaces 为一个列表,其中包含在 realm公开的每个接口

  2. interfaces 进行排序,使得如果 ABinterfaces, 并且 A 继承B,则 Ainterfaces 中的索引高于 B

  3. 对于 interfaces 中的每个 interface

    1. 如果 interface 未使用 [LegacyNoInterfaceObject] 或 [LegacyNamespace] 扩展属性声明,则:

      1. idinterface标识符

      2. interfaceObject 为在 realm 中使用 idinterface 创建接口对象的结果。

      3. 执行 DefineMethodProperty(target, id, interfaceObject, false)。

      4. 如果 interface 使用 [LegacyWindowAlias] 扩展属性声明, 并且 target 实现 Window 接口,则:

        1. 对于 [LegacyWindowAlias] 的 标识符中的每个标识符 id

          1. 执行 DefineMethodProperty(target, id, interfaceObject, false)。

    2. 如果 interface 使用 [LegacyFactoryFunction] 扩展属性声明, 则:

      1. 对于 [LegacyFactoryFunction] 的 标识符中的每个标识符 id

        1. legacyFactoryFunction 为在 realm 中为 interface 使用 id 创建旧式工厂函数的结果。

        2. 执行 DefineMethodProperty(target, id, legacyFactoryFunction, false)。

  4. 对于realm公开并且定义了常量的每个回调接口 interface

    1. idinterface标识符

    2. interfaceObject 为在 realm 中使用 idinterface 创建旧式回调接口对象的结果。

    3. 执行 DefineMethodProperty(target, id, interfaceObject, false)。

  5. 对于realm公开的每个命名空间 namespace

    1. idnamespace标识符

    2. namespaceObject 为在 realm 中为 namespace 创建命名空间对象的结果。

    3. 执行 DefineMethodProperty(target, id, namespaceObject, false)。

平台对象实现的接口集在对象的生命周期内不会改变。

具有不同全局对象的多个平台对象将在其 [[PrimaryInterface]] 内部插槽中共享对同一接口的引用。例如,一个页面可以包含一个同源 iframe,iframe 的方法可以在主页面的同类元素上调用,而不会引发异常。

接口混入不直接参与实现算法的评估。相反,接口混入包含的每个接口都有其自己的接口混入的每个成员的“副本”,并且相应的操作函数检查接收者实现包含接口混入的特定接口

平台对象主接口是该对象的 [[PrimaryInterface]] 内部插槽的值,它是它实现的派生程度最高的接口

给定平台对象关联的领域可以在创建后更改。当与平台对象关联的领域更改时,其 [[Prototype]] 内部插槽必须立即更新为平台对象新关联的领域主接口接口原型对象

此外,实现具有 [Global] 扩展属性接口平台对象会从以下位置声明性地获取属性:

而应以命令方式定义这些属性。

3.8.1. [[SetPrototypeOf]]

当使用 JavaScript 语言值 V 调用实现带有 [Global] 扩展属性接口平台对象 O 的 [[SetPrototypeOf]] 内部方法时,将执行以下步骤:

  1. 如果 O关联领域全局原型链是否可变为 true, 则返回 ? OrdinarySetPrototypeOf(O, V)。

  2. 返回 ? SetImmutablePrototype(O, V)。

注意:对于 Window 对象,无法观察到此方法是否已实现,因为 WindowProxy 对象的存在确保了 [[SetPrototypeOf]] 永远不会直接在 Window 对象上调用。但是,对于其他全局对象,这是必需的。

3.9. 旧版平台对象

遗留平台对象会显示具有与其索引属性命名属性对应的附加属性。这些属性并不是对象上的“真实”自有属性,而是通过[[GetOwnProperty]] 内部方法使它们看起来像是自有属性。

一个对象可以实现多个支持索引属性的接口。但是,如果这样做,并且对于对象的支持的属性索引存在冲突定义,则对象看起来具有哪些附加属性或其索引属性的确切行为是未定义的。命名属性的情况同样适用。

由遗留平台对象实现的派生接口上定义的索引属性获取器是定义对象以数组索引进行索引时的行为的获取器。对于索引属性设置器也是如此。这样,来自祖先接口的这些特殊操作的定义可以被覆盖。

如果某个平台对象 O 实现了具有某标识符的接口,且该接口成员在任何 O 实现的接口中为不可伪造,则该属性名称为给定平台对象上的不可伪造属性名称

对于getter的支持在§ 3.9.1 [[GetOwnProperty]]中处理,而对于setter的支持在§ 3.9.3 [[DefineOwnProperty]]§ 3.9.2 [[Set]]中处理。

此外,遗留平台对象还具有以下定义的内部方法:

3.9.1. [[GetOwnProperty]]

每个遗留平台对象 O 的 [[GetOwnProperty]] 内部方法在被调用时,传入属性名称 P,必须按如下行为执行:

  1. 返回 ? LegacyPlatformObjectGetOwnProperty(O, P, false)。

3.9.2. [[Set]]

每个遗留平台对象 O 的 [[Set]] 内部方法在被调用时,传入属性名称 P,值 V,以及 JavaScript 语言值 Receiver,必须按如下行为执行:

  1. 如果 OReceiver 是同一个对象,则:

    1. 如果 O 实现了具有索引属性设置器的接口,并且 P 是一个数组索引,则:

      1. 调用索引属性设置器,传入 OPV

      2. 返回 true

    2. 如果 O 实现了一个带有命名属性设置器的接口,并且 P 是一个字符串,那么:

      1. 调用命名属性设置器,传入 OPV

      2. 返回 true

  2. ownDesc? LegacyPlatformObjectGetOwnProperty(O, P, true)。

  3. 执行 ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc)。

3.9.3. [[DefineOwnProperty]]

当一个遗留平台对象 O 的 [[DefineOwnProperty]] 内部方法被调用时,传入属性键 P属性描述符 Desc,应按以下步骤进行:

  1. 如果 O 支持索引属性 并且 P 是数组索引,则:

    1. 如果调用 IsDataDescriptor(Desc) 的结果是 false,则返回 false

    2. 如果 O 没有实现具有索引属性设置器的接口,则返回 false

    3. 调用索引属性设置器,传入 OPDesc.[[Value]]。

    4. 返回 true

  2. 如果 O 支持命名属性O 没有实现带有 [Global] 扩展属性接口P 是一个字符串, 并且 P 不是 O不可伪造属性名, 那么:

    1. 如果 P 不是支持的属性名,则设 creating 为 true,否则设为 false。

    2. 如果 O 实现了具有 [LegacyOverrideBuiltIns] 扩展属性 的接口,或 O 没有名为 P 的自有属性,则:

      1. 如果 creating 为 false 并且 O 没有实现具有 命名属性设置器 的接口,则返回 false

      2. 如果 O 实现了具有 命名属性设置器 的接口,则:

        1. 如果调用 IsDataDescriptor(Desc) 的结果是 false,则返回 false

        2. 调用命名属性设置器,传入 OPDesc.[[Value]]。

        3. 返回 true

  3. 返回 ! OrdinaryDefineOwnProperty(O, P, Desc)。

3.9.4. [[Delete]]

当每个遗留平台对象 O 的 [[Delete]] 内部方法被调用时,传入属性名称 P,应按以下步骤进行。

  1. 如果 O 支持索引属性 并且 P 是数组索引,则:

    1. index 为调用 ! ToUint32(P) 的结果。

    2. 如果 index 不是 支持的属性索引,则返回 true

    3. 返回 false

  2. 如果 O 支持命名属性O 没有实现带有 [Global] 扩展属性接口,并且调用带有属性名 P 和对象 O命名属性可见性算法 的结果为 true,则:

    1. 如果 O 没有实现具有 命名属性删除器 的接口,则返回 false

    2. operation 为用于声明命名属性删除器的操作。

    3. 如果 operation 没有定义标识符,则:

      1. 执行接口描述中列出的步骤,删除现有命名属性,名称为 P

      2. 如果步骤指示删除失败,则返回 false

    4. 否则,operation 使用标识符定义:

      1. 执行 operation方法步骤,将 O 作为 this,将 « P » 作为参数值。

      2. 如果 operation 被声明为具有 返回类型 boolean,并且步骤返回 false,则返回 false

    5. 返回 true

  3. 如果 O 有名为 P 的自有属性,则:

    1. 如果该属性不可配置,则返回 false

    2. 否则,从 O 中删除该属性。

  4. 返回 true

3.9.5. [[PreventExtensions]]

当遗留平台对象的 [[PreventExtensions]] 内部方法被调用时,执行以下步骤:

  1. 返回 false

注意:这使得遗留平台对象保持可扩展性,因为对它们来说 [[PreventExtensions]] 会失败。

3.9.6. [[OwnPropertyKeys]]

本文档没有为实现接口平台对象(或代表异常的平台对象)定义完整的属性枚举顺序。 然而,它通过如下定义 [[OwnPropertyKeys]] 内部方法为旧版平台对象定义了该顺序。

当调用旧版平台对象 O 的 [[OwnPropertyKeys]] 内部方法时, 将执行以下步骤:

  1. keys 为一个新的空 JavaScript 字符串和 Symbol 值列表

  2. 如果 O 支持索引属性,则对于 O支持的属性索引中的每个 index,按升序数字顺序,追加 ! ToString(index) 到 keys

  3. 如果 O 支持命名属性,则对于 O支持的属性名称中根据命名属性可见性算法可见的每个 P追加 Pkeys

  4. 对于 O 自己的属性键中作为字符串的每个 P,按属性创建的时间先后顺序,追加 Pkeys

  5. 对于 O 自己的属性键中作为 Symbol 的每个 P,按属性创建的时间先后顺序,追加 Pkeys

  6. 断言:keys 没有重复项。

  7. 返回 keys

3.9.7. 抽象操作

要确定属性名 P 是否数组索引,应用以下算法:

  1. 如果 P 不是字符串,则返回 false

  2. indexCanonicalNumericIndexString(P)。

  3. 如果 indexundefined,则返回 false

  4. 如果 IsInteger(index) 是 false,则返回 false

  5. 如果 index 是 −0,则返回 false

  6. 如果 index < 0,则返回 false

  7. 如果 index ≥ 232 − 1,则返回 false

    注意: 232 − 1 是 JavaScript 允许的最大数组长度。

  8. 返回 true

命名属性可见性算法用于确定给定的命名属性是否在对象上公开。某些命名属性是否公开取决于是否使用了 [LegacyOverrideBuiltIns] 扩展属性。该算法如下操作,属性名为 P,对象为 O

  1. 如果 P 不是 O 的一个 受支持的属性名称,则返回 false。

  2. 如果 O 有一个名为 P 的自身属性,则返回 false。

    注意:这将包括 O 具有不可伪造属性的情况,因为实际上这些属性总是在对象有任何受支持的属性名称之前设置的,一旦设置,它们将使相应的命名属性不可见。

  3. 如果 O 实现了一个具有 [LegacyOverrideBuiltIns] 扩展属性 的接口,则返回 true。

  4. prototypeO.[[GetPrototypeOf]]()。

  5. prototype 不为 null 时:

    1. 如果 prototype 不是一个 命名属性对象,并且 prototype 有一个名为 P 的自身属性,则返回 false。

    2. prototype 设置为 prototype.[[GetPrototypeOf]]()。

  6. 返回 true。

这确保了对于具有命名属性的对象,属性解析按照以下顺序进行:

  1. 索引属性。

  2. 自身属性,包括不可伪造的属性和操作。

  3. 然后,如果 [LegacyOverrideBuiltIns]:

    1. 命名属性。

    2. 来自原型链的属性。

  4. 否则,如果不使用 [LegacyOverrideBuiltIns]:

    1. 来自原型链的属性。

    2. 命名属性。

要在平台对象 O 上,使用属性名 P 和 JavaScript 值 V调用索引属性设置器,必须执行以下步骤:

  1. index 为调用 ? ToUint32(P) 的结果。

  2. 如果 index 不是 受支持的属性索引,则设 creating 为 true,否则设为 false。

  3. operation 为用于声明索引属性设置器的操作。

  4. Toperation 的第二个参数的类型。

  5. value 为将 V 转换为类型为 T 的 IDL 值的结果,使用 转换

  6. 如果 operation 是在没有 标识符 的情况下定义的:

    1. 如果 creating 为 true,则执行接口描述中列出的步骤,以 index 作为索引,value 作为值来 设置新索引属性的值

    2. 否则,creating 为 false。执行接口描述中列出的步骤,以 index 作为索引,value 作为值来 设置现有索引属性的值

  7. 否则,operation 是用标识符定义的。执行 operation方法步骤,以 O 作为 this,« index, value » 作为参数值。

要在平台对象 O 上,使用属性名 P 和 JavaScript 值 V调用命名属性设置器,必须执行以下步骤:

  1. 如果 P 不是 受支持的属性名称,则设 creating 为 true,否则为 false。

  2. operation 为用于声明命名属性设置器的操作。

  3. Toperation 的第二个参数的类型。

  4. value 为将 V 转换为类型为 T 的 IDL 值的结果,使用 转换

  5. 如果 operation 是在没有 标识符 的情况下定义的:

    1. 如果 creating 为 true,则执行接口描述中列出的步骤,以 P 作为名称,value 作为值来 设置新命名属性的值

    2. 否则,creating 为 false。执行接口描述中列出的步骤,以 P 作为名称,value 作为值来 设置现有命名属性的值

  6. 否则,operation 是用标识符定义的。执行 operation方法步骤,以 O 作为 this,« P, value » 作为参数值。

LegacyPlatformObjectGetOwnProperty 抽象操作在使用对象 O、属性名 P 和布尔值 ignoreNamedProps 调用时,执行以下步骤:

  1. 如果 O 支持索引属性P 是一个数组索引,则:

    1. index 为调用 ! ToUint32(P) 的结果。

    2. 如果 index受支持的属性索引,则:

      1. operation 为用于声明索引属性 getter 的操作。

      2. value 为一个未初始化的变量。

      3. 如果 operation 是在没有 标识符 的情况下定义的,则将 value 设为执行接口描述中列出的步骤的结果,用于 确定索引属性的值,索引为 index

      4. 否则,operation 是用标识符定义的。将 value 设为执行 operation方法步骤 的结果,以 O 作为 this,« index » 作为参数值。

      5. desc 为一个新创建的 属性描述符,没有字段。

      6. desc.[[Value]] 设置为将 value 转换为 JavaScript 值的结果。

      7. 如果 O 实现 了带有 索引属性 setter 的接口,则将 desc.[[Writable]] 设为 true,否则设为 false

      8. desc.[[Enumerable]] 和 desc.[[Configurable]] 设为 true

      9. 返回 desc

    3. ignoreNamedProps 设为 true。

  2. 如果 O 支持命名属性ignoreNamedProps 为 false,则:

    1. 如果运行 命名属性可见性算法,传入属性名 P 和对象 O,结果为 true,则:

      1. operation 为用于声明命名属性 getter 的操作。

      2. value 为一个未初始化的变量。

      3. 如果 operation 是在没有 标识符 的情况下定义的,则将 value 设为执行接口描述中列出的步骤的结果,用于 确定命名属性的值,名称为 P

      4. 否则,operation 是用标识符定义的。将 value 设为执行 operation方法步骤 的结果,以 O 作为 this,« P » 作为参数值。

      5. desc 为一个新创建的 属性描述符,没有字段。

      6. desc.[[Value]] 设为 转换 value 为 JavaScript 值的结果。

      7. 如果 O 实现 了带有 命名属性 setter 的接口,则将 desc.[[Writable]] 设为 true,否则设为 false

      8. 如果 O 实现 了带有 [LegacyUnenumerableNamedProperties] 扩展属性 的接口,则将 desc.[[Enumerable]] 设为 false,否则设为 true

      9. desc.[[Configurable]] 设为 true

      10. 返回 desc

  3. 返回 OrdinaryGetOwnProperty(O, P)。

3.10. 可观察数组奇异对象

可观察数组的特殊对象是一种特定类型的 JavaScript 代理特殊对象,通过本节定义的代理陷阱创建。它们之所以这样定义,是因为 JavaScript 规范对拥有Array实例作为代理目标的代理特殊对象进行特殊处理,我们希望确保可观察数组类型以这种特殊处理暴露给 JavaScript 代码。

可观察数组的特殊对象所使用的代理陷阱,旨在确保超出普通Array实例的若干不变量:

要在realm realm 中创建一个可观察数组的特殊对象,给定 Web IDL 类型 T 以及算法 setAlgorithmdeleteAlgorithm
  1. innerArray! ArrayCreate(0)。

  2. handlerOrdinaryObjectCreate(null, « [[Type]], [[SetAlgorithm]], [[DeleteAlgorithm]], [[BackingList]] »)。

  3. handler.[[Type]] 设为 T

  4. handler.[[SetAlgorithm]] 设为 setAlgorithm

  5. handler.[[DeleteAlgorithm]] 设为 deleteAlgorithm

  6. definePropertyCreateBuiltinFunction(§ 3.10.1 defineProperty中的步骤, « », realm)。

  7. 执行 ! CreateDataPropertyOrThrow(handler, "defineProperty", defineProperty)。

  8. deletePropertyCreateBuiltinFunction(§ 3.10.2 deleteProperty中的步骤, « », realm)。

  9. 执行 ! CreateDataPropertyOrThrow(handler, "deleteProperty", deleteProperty)。

  10. getCreateBuiltinFunction(§ 3.10.3 get中的步骤, « », realm)。

  11. 执行 ! CreateDataPropertyOrThrow(handler, "get", get)。

  12. getOwnPropertyDescriptorCreateBuiltinFunction(§ 3.10.4 getOwnPropertyDescriptor中的步骤, « », realm)。

  13. 执行 ! CreateDataPropertyOrThrow(handler, "getOwnPropertyDescriptor", getOwnPropertyDescriptor)。

  14. hasCreateBuiltinFunction(§ 3.10.5 has 中的步骤, « », realm)。

  15. 执行 ! CreateDataPropertyOrThrow(handler, "has", has)。

  16. ownKeysCreateBuiltinFunction(§ 3.10.6 ownKeys 中的步骤, « », realm)。

  17. 执行 ! CreateDataPropertyOrThrow(handler, "ownKeys", ownKeys)。

  18. preventExtensionsCreateBuiltinFunction(§ 3.10.7 preventExtensions 中的步骤, « », realm)。

  19. 执行 ! CreateDataPropertyOrThrow(handler, "preventExtensions", preventExtensions)。

  20. setCreateBuiltinFunction(§ 3.10.8 set 中的步骤, « », realm)。

  21. 执行 ! CreateDataPropertyOrThrow(handler, "set", set)。

  22. 返回 ! ProxyCreate(innerArray, handler)。

3.10.1. defineProperty

对于可观察数组特殊对象,给定 OPdescriptorObj,其 defineProperty 代理陷阱的步骤如下:

  1. handlerthis 值。

  2. descriptor! ToPropertyDescriptor(descriptorObj)。

  3. 如果 P 为 "length",则:

    1. 如果 IsAccessorDescriptor(descriptor) 为 true,则返回 false

    2. 如果 descriptor.[[Configurable]] 存在且值为 true,则返回 false

    3. 如果 descriptor.[[Enumerable]] 存在且值为 true,则返回 false

    4. 如果 descriptor.[[Writable]] 存在且值为 false,则返回 false

    5. 如果 descriptor.[[Value]] 存在,则返回调用设置长度给定 handlerdescriptor.[[Value]] 的结果。

    6. 返回 true

  4. 如果 P 是一个数组索引,则:

    1. 如果 IsAccessorDescriptor(descriptor) 为 true,则返回 false

    2. 如果 descriptor.[[Configurable]] 存在且值为 false,则返回 false

    3. 如果 descriptor.[[Enumerable]] 存在且值为 false,则返回 false

    4. 如果 descriptor.[[Writable]] 存在且值为 false,则返回 false

    5. 如果 descriptor.[[Value]] 存在,则返回调用设置索引值给定 handlerPdescriptor.[[Value]] 的结果。

    6. 返回 true

  5. 返回 ? O.[[DefineOwnProperty]](P, descriptor)。

3.10.2. deleteProperty

对于可观察数组特殊对象,给定 OP,其 deleteProperty 代理陷阱的步骤如下:
  1. handlerthis 值。

  2. 如果 P 为 "length",则返回 false

  3. 如果 P 是一个数组索引,则:

    1. oldLenhandler.[[BackingList]] 的 大小

    2. index! ToUint32(P) 的结果。

    3. 如果 indexoldLen − 1,则返回 false

    4. 根据 handler.[[DeleteAlgorithm]],执行算法步骤,给定 handler.[[BackingList]][index] 和 index

    5. 移除 handler.[[BackingList]] 的最后一项。

    6. 返回 true

  4. 返回 ? O.[[Delete]](P)。

3.10.3. get

对于可观察数组特殊对象,给定 OPReceiver,其 get 代理陷阱的步骤如下:
  1. handlerthis 值。

  2. lengthhandler.[[BackingList]] 的 大小

  3. 如果 P 为 "length",则返回 length

  4. 如果 P 是一个数组索引,则:

    1. index! ToUint32(P) 的结果。

    2. 如果 indexlength,则返回 undefined

    3. jsValue 为通过 转换 handler.[[BackingList]][index] 为 JavaScript 值的结果。

    4. 断言:上述步骤不会抛出异常。

    5. 返回 jsValue

  5. 返回 ? O.[[Get]](P, Receiver)。

3.10.4. getOwnPropertyDescriptor

对于可观察数组奇异对象,给定 OP,其 getOwnPropertyDescriptor 代理捕获器的步骤如下:
  1. handlerthis 值。

  2. lengthhandler.[[BackingList]] 的大小

  3. 如果 P 是 "length",则返回 ! FromPropertyDescriptor(PropertyDescriptor{[[Configurable]]: false, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: length })。

  4. 如果 P 是一个数组索引,则

    1. index! ToUint32(P)。

    2. 如果 indexlength,则返回 undefined

    3. jsValue 为将 handler.[[BackingList]][index] 转换为 JavaScript 值的结果。

    4. 断言:以上步骤永远不会抛出异常。

    5. 返回 FromPropertyDescriptor(PropertyDescriptor{[[Configurable]]: true, [[Enumerable]]: true, [[Writable]]: true, [[Value]]: jsValue })。

  5. 返回 FromPropertyDescriptor(? O.[[GetOwnProperty]](P))。

3.10.5. has

对于可观察数组奇异对象,给定 OP,其 has 代理捕获器的步骤如下:
  1. handlerthis 值。

  2. 如果 P 是 "length",则返回 true

  3. 如果 P 是一个数组索引,则:

    1. index! ToUint32(P)。

    2. 如果 index < handler.[[BackingList]] 的大小, 则返回 true

    3. 返回 false

  4. 返回 ? O.[[HasProperty]](P)。

3.10.6. ownKeys

对于可观察数组奇异对象,给定 O,其 ownKeys 代理捕获器的步骤如下:
  1. handlerthis 值。

  2. lengthhandler.[[BackingList]] 的大小

  3. keys 为一个空列表

  4. i 为 0。

  5. i < length 时:

    1. 追加 ! ToString(i) 到 keys

    2. i 设置为 i + 1。

  6. 扩展 keys,使用 ! O.[[OwnPropertyKeys]]()。

  7. 返回 CreateArrayFromList(keys)。

3.10.7. preventExtensions

对于可观察数组奇异对象,其 preventExtensions 代理捕获器的步骤如下:
  1. 返回 false

3.10.8. set

对于可观察数组奇异对象,给定 OPVReceiver,其 set 代理捕获器的步骤如下:
  1. handlerthis 值。

  2. 如果 P 是 "length",则返回设置长度的结果,给定 handlerV

  3. 如果 P 是一个数组索引,则返回设置索引值的结果,给定 handlerPV

  4. 返回 ? O.[[Set]](P, V, Receiver)。

3.10.9. 抽象操作

设置可观察数组奇异对象的长度,给定 handlernewLen
  1. uint32Len? ToUint32(newLen)。

  2. numberLen? ToNumber(newLen)。

  3. 如果 uint32LennumberLen,则抛出 RangeError 异常。

  4. oldLenhandler.[[BackingList]] 的大小

  5. 如果 uint32Len > oldLen,则返回 false

  6. indexToDeleteoldLen − 1。

  7. indexToDeleteuint32Len 时:

    1. 执行 handler.[[DeleteAlgorithm]] 给出的算法步骤,给定 handler.[[BackingList]][indexToDelete] 和 indexToDelete

    2. 移除 handler.[[BackingList]] 中的最后一项。

    3. indexToDelete 设置为 indexToDelete − 1。

  8. 返回 true

设置可观察数组奇异对象的索引值,给定 handlerPV
  1. oldLenhandler.[[BackingList]] 的大小

  2. index! ToUint32(P)。

  3. 如果 index > oldLen,返回 false

  4. idlValue 为将 V 转换handler.[[Type]] 给出的类型的结果。

  5. 如果 index < oldLen,则:

    1. 执行 handler.[[DeleteAlgorithm]] 给出的算法步骤,给定 handler.[[BackingList]][index] 和 index

  6. 执行 handler.[[SetAlgorithm]] 给出的算法步骤,给定 idlValueindex

  7. 如果 index = oldLen,则追加 idlValuehandler.[[BackingList]]。

  8. 否则,将 handler.[[BackingList]][index] 设置为 idlValue

  9. 返回 true

3.11. 回调接口

§ 2.12 实现接口的对象 中所述,回调接口可以由任何 JavaScript 对象在脚本中实现。 以下情况说明了如何在给定对象上调用回调接口操作

注意,JavaScript 对象不需要具有与常量对应的属性,也可以被视为实现了带有常量声明的回调接口

Web IDL 参数列表是一个由值组成的列表,其中每个值要么是 IDL 值,要么是表示缺失可选参数的特殊值“缺失”。

要将Web IDL 参数列表转换为 JavaScript 参数列表,给定一个Web IDL 参数列表 args,执行以下步骤:
  1. jsArgs 为一个空的列表

  2. i 为 0。

  3. count 为 0。

  4. i < args大小时:

    1. 如果 args[i] 是特殊值“缺失”,则追加undefinedjsArgs

    2. 否则,args[i] 是一个 IDL 值:

      1. convertResult args[i] 转换为 JavaScript 值的结果。重新抛出任何异常。

      2. 追加 convertResultjsArgs

      3. count 设置为 i + 1。

    3. i 设置为 i + 1。

  5. jsArgs 截短至包含 count 个项。

  6. 返回 jsArgs

调用用户对象的操作,给定一个回调接口类型value,操作名称 opNameWeb IDL 参数列表 args,以及可选的回调 this 值 thisArg,执行以下步骤。这些步骤将返回一个 IDL 值或抛出一个异常。

  1. completion 为一个未初始化的变量。

  2. 如果未提供 thisArg,则将 thisArg 设置为 undefined

  3. O 为与 value 对应的 JavaScript 对象。

  4. realmO关联领域

  5. relevant settingsrealm设置对象

  6. stored settingsvalue回调上下文

  7. 使用 relevant settings 准备运行脚本

  8. 使用 stored settings 准备运行回调

  9. XO

  10. 如果 IsCallable(O) 为假,则:

    1. getResultCompletion(Get(O, opName))。

    2. 如果 getResult 是一个突然完成,则将 completion 设置为 getResult 并跳转到标记为 返回 的步骤。

    3. X 设置为 getResult.[[Value]]。

    4. 如果 IsCallable(X) 为 false, 则将 completion 设置为 Completion Record { [[Type]]: throw, [[Value]]: 一个 新创建的 TypeError 对象, [[Target]]: empty },并跳转 到标记为 返回 的步骤。

    5. thisArg 设置为 O (覆盖提供的值)。

  11. jsArgs args 转换为 JavaScript 参数列表的结果。如果此步骤抛出异常,则将 completion 设置为表示所抛出异常的完成值,并跳转到标记为返回的步骤。

  12. callResultCompletion(Call(X, thisArg, jsArgs))。

  13. 如果 callResult 是一个突然完成,则将 completion 设置为 callResult 并跳转到标记为 返回 的步骤。

  14. completion 设置为将 callResult.[[Value]] 转换为与操作返回类型相同的 IDL 值的结果。如果此操作抛出异常,则将 completion 设置为表示所抛出异常的完成值。

  15. 返回:此时 completion 将被设置为一个 IDL 值或一个突然完成

    1. 使用 stored settings 在运行回调后进行清理

    2. 使用 relevant settings 在运行脚本后进行清理

    3. 如果 completion 是一个 IDL 值,则返回 completion

    4. 如果 completion 是一个突然完成,并且操作的返回类型不是promise 类型,则抛出 completion.[[Value]]。

    5. rejectedPromise! Call(%Promise.reject%, %Promise%, «completion.[[Value]]»)。

    6. 返回将 rejectedPromise 转换为操作返回类型的结果。

对于在给定领域公开且定义了常量的每个回调接口, 在领域全局对象上存在一个对应的属性。 该属性的名称是回调接口标识符, 其值是一个称为旧版回调接口对象的对象。

给定回调接口旧版回调接口对象是一个内置函数对象。 它具有与该接口上定义的常量相对应的属性, 如§ 3.7.5 常量部分所述。

注意:由于旧版回调接口对象是一个函数对象,因此当将 typeof 运算符应用于旧版回调接口对象时,它将返回 "function"。

给定回调接口 interface(其标识符id,且位于领域 realm 中),其旧版回调接口对象按如下方式创建

  1. steps 为以下步骤:

    1. 抛出一个 TypeError

  2. FCreateBuiltinFunction(steps, « », realm)。

  3. 执行 SetFunctionName(F, id)。

  4. 执行 SetFunctionLength(F, 0)。

  5. 在给定 realm 的情况下,在 F定义 interface 的常量

  6. 返回 F

3.12. 调用回调函数

一个用作回调函数值的 JavaScript 可调用对象的调用方式,与回调接口值上的操作的调用方式类似(如上一节所述)。

调用一个回调函数类型callable,使用 Web IDL 参数列表 args、 异常行为 exceptionBehavior(“report”或“rethrow”), 以及可选的回调 this 值 thisArg, 执行以下步骤。 这些步骤将返回一个 IDL 值或抛出一个异常。

exceptionBehavior 参数当且仅当 callable返回类型不是promise 类型时才必须提供。如果 callable 的返回类型既不是 undefined 也不是 any,则它必须是“rethrow”。

在调用点更新以遵守此规定之前,未能在此处提供强制性值的规范应被理解为提供“rethrow”。
  1. completion 为一个未初始化的变量。

  2. 如果未给出 thisArg,则令 thisArgundefined

  3. F 为与 callable 对应的 JavaScript 对象。

  4. 如果 IsCallable(F) 为 false

    1. 注意:这仅在回调函数来自标记有 [LegacyTreatNonObjectAsNull] 的属性时才可能发生。

    2. 返回将 undefined 转换为回调函数返回类型的结果。

  5. realmF关联领域

  6. relevant settingsrealm设置对象

  7. stored settingscallable回调上下文

  8. 使用 relevant settings 准备运行脚本

  9. 使用 stored settings 准备运行回调

  10. jsArgs 为将 args 转换为 JavaScript 参数列表的结果。如果此操作抛出异常,则将 completion 设置为表示所抛出异常的完成值,并跳转到标记为 返回 的步骤。

  11. callResultCompletion(Call(F, thisArg, jsArgs))。

  12. 如果 callResult 是一个突然完成,则将 completion 设置为 callResult 并跳转到标记为 返回 的步骤。

  13. completion 设置为将 callResult.[[Value]] 转换为与 callable 返回类型相同的 IDL 值的结果。如果此操作抛出异常,则将 completion 设置为表示所抛出异常的完成值。

  14. 返回:此时 completion 将被设置为一个 IDL 值或一个突然完成

    1. 使用 stored settings 在运行回调后进行清理

    2. 使用 relevant settings 在运行脚本后进行清理

    3. 如果 completion 是一个 IDL 值,则返回 completion

    4. 断言completion 是一个突然完成

    5. 如果 exceptionBehavior 是“rethrow”,则抛出 completion.[[Value]]。

    6. 否则,如果 exceptionBehavior 是“report”:

      1. 断言callable返回类型undefinedany

      2. realm全局对象报告异常 completion.[[Value]]。

      3. 返回唯一的 undefined IDL 值。

    7. 断言callable返回类型是一个promise 类型

    8. rejectedPromise! Call(%Promise.reject%, %Promise%, «completion.[[Value]]»)。

    9. 返回将 rejectedPromise 转换为回调函数返回类型的结果。

一些回调函数 invece 用作构造函数。此类回调函数的返回类型不得为promise 类型

构造一个回调函数类型callable,使用 Web IDL 参数列表 args, 执行以下步骤。 这些步骤将返回一个 IDL 值或抛出一个异常。

  1. completion 为一个未初始化的变量。

  2. F 为与 callable 对应的 JavaScript 对象。

  3. 如果 IsConstructor(F) 为 false, 则抛出 TypeError 异常。

  4. realmF关联领域

  5. relevant settingsrealm设置对象

  6. stored settingscallable回调上下文

  7. 使用 relevant settings 准备运行脚本

  8. 使用 stored settings 准备运行回调

  9. jsArgs 为将 args 转换为 JavaScript 参数列表的结果。如果此操作抛出异常,则将 completion 设置为表示所抛出异常的完成值,并跳转到标记为 返回 的步骤。

  10. callResultCompletion(Construct(F, jsArgs))。

  11. 如果 callResult 是一个突然完成,则将 completion 设置为 callResult 并跳转到标记为 返回 的步骤。

  12. completion 设置为将 callResult.[[Value]] 转换为与 callable 返回类型相同的 IDL 值的结果。如果此操作抛出异常,则将 completion 设置为表示所抛出异常的完成值。

  13. 返回:此时 completion 将被设置为一个 IDL 值或一个突然完成

    1. 使用 stored settings 在运行回调后进行清理

    2. 使用 relevant settings 在运行脚本后进行清理

    3. 如果 completion 是一个突然完成,则抛出 completion.[[Value]]。

    4. 返回 completion

3.13. 命名空间

对于每一个在给定的命名空间暴露领域,在该领域全局对象上存在相应的属性。该属性的名称为命名空间的标识符,其值是一个称为命名空间对象的对象。

命名空间对象的特性在 § 3.13.1 命名空间对象 中描述。

3.13.1. 命名空间对象

为给定的命名空间 namespace领域 realm 创建命名空间对象,步骤如下:

  1. namespaceObjectOrdinaryObjectCreate(realm.[[Intrinsics]].[[%Object.prototype%]])。

  2. 定义 namespacenamespaceObject 上的常规属性,给定 realm

  3. 定义 namespacenamespaceObject 上的常规操作,给定 realm

  4. 定义 namespacenamespaceObject 上的常量,给定 realm

  5. 对于每一个具有 [LegacyNamespace] 扩展属性且其参数为 namespace 标识符的 暴露 接口 interface

    1. idinterface标识符

    2. interfaceObject创建 interface 的接口对象的结果,给定 idrealm

    3. 执行 DefineMethodProperty(namespaceObject, id, interfaceObject, false)。

  6. 返回 namespaceObject

命名空间对象类字符串是该命名空间标识符

3.14. 异常

3.14.1. DOMException 自定义绑定

在 JavaScript 绑定中,DOMException接口原型对象的 [[Prototype]] 内部插槽被设置为内在对象 %Error.prototype%, 正如在创建接口原型对象抽象操作中所定义的。 它也像所有内置异常一样,拥有一个 [[ErrorData]] 插槽。

此外,如果一个实现赋予了原生的 Error 对象特殊的能力或非标准属性(例如 stack 属性), 它也应该在 DOMException 对象上公开这些能力或属性。

3.14.2. 异常对象

简单异常由相应类型的原生 JavaScript 对象表示。

一个 DOMException 由一个实现了 DOMException 接口的平台对象表示。

3.14.3. 创建和抛出异常

创建一个类型为 T简单异常
  1. message 为一个适合异常情况的实现定义的消息。调用规范可能包含帮助实现构建此消息的信息。

    实现在构建此消息时需要小心,不要泄露敏感或受保护的信息,例如,通过包含跨源框架的 URL 或可能识别用户的信息。

  2. args 为 « message »。

  3. constructor当前领域.[[Intrinsics]].[[%T%]]。

  4. 返回 ! Construct(constructor, args)。

创建一个 DOMException,给定一个字符串 name
  1. 断言:name 出现在DOMException 名称表中。

  2. ex 为一个在当前领域创建的 DOMException

  3. exname 设置为 name

  4. exmessage 设置为一个适合异常情况的实现定义的消息。调用规范可能包含帮助实现构建此消息的信息。

    实现在构建此消息时需要小心,不要泄露敏感或受保护的信息,例如,通过包含跨源框架的 URL 或可能识别用户的信息。

  5. 返回 ex

创建一个 DOMException 派生接口,给定接口标识符 type 和额外的初始化指令:
  1. ex 为一个在当前领域中创建的,由 type 标识的接口实例。

  2. exname 设置为 type

  3. exmessage 设置为一个适合异常情况的实现定义的消息。调用规范可能包含帮助实现构建此消息的信息。

    实现在构建此消息时需要小心,不要泄露敏感或受保护的信息,例如,通过包含跨源框架的 URL 或可能识别用户的信息。

  4. 按照调用者的描述,对 ex 执行任何额外的初始化。

  5. 返回 ex

抛出一个异常
  1. O 为使用相同参数创建异常的结果。

  2. 抛出 O

上述算法限制了从函数对象传播出来的表示异常的对象,必须是与该函数对象领域相关联的对象(即,函数执行时的当前领域)。例如,考虑以下 IDL:

[Exposed=Window]
interface MathUtils {
    // 如果 x 为负,则抛出 "NotSupportedError" DOMExceptiondouble computeSquareRoot(double x);
};

如果我们将 computeSquareRoot 应用于来自不同领域MathUtils 对象,那么抛出的异常将来自方法的领域,而不是应用该方法的对象:

const myMU = window.getMathUtils();          // 来自此领域的 MathUtils 对象
const otherMU = otherWindow.getMathUtils();  // 来自不同领域的 MathUtils 对象

myMU instanceof Object;                      // 计算结果为 true。
otherMU instanceof Object;                   // 计算结果为 false。
otherMU instanceof otherWindow.Object;       // 计算结果为 true。

try {
    otherMU.doComputation.call(myMU, -1);
} catch (e) {
    console.assert(!(e instanceof DOMException));
    console.assert(e instanceof otherWindow.DOMException);
}

3.14.4. 处理异常

除非另有规定, 每当由于本文档中的要求而调用 JavaScript 运行时语义并 由于抛出异常而结束时,该异常 必须传播给调用者,如果未在那里捕获,则传播给其调用者,依此类推。

根据文档约定,本文档中指定的算法可以拦截抛出的异常,方法是指定在抛出异常时要采取的确切步骤,或者显式处理突然完成

以下IDL 片段定义了两个接口和一个异常ExceptionThrower 上的 valueOf 属性被定义为在尝试获取其值时抛出异常。

[Exposed=Window]
interface Dahut {
    attribute DOMString type;
};

[Exposed=Window]
interface ExceptionThrower {
    // 此属性始终抛出 NotSupportedError 并且从不返回值。
    attribute long valueOf;
};

假设一个支持此接口的 JavaScript 实现, 以下代码演示了如何处理异常:

var d = getDahut();              // 获取 Dahut 的一个实例。
var et = getExceptionThrower();  // 获取 ExceptionThrower 的一个实例。

try {
    d.type = { toString: function() { throw "abc"; } };
} catch (e) {
    // 字符串 "abc" 在这里被捕获,因为在从原生对象转换为字符串的过程中,
    // 调用了匿名函数,并且 [[DefaultValue]]、ToPrimitive 或
    // ToString 算法都没有定义为捕获该异常。
}

try {
    d.type = { toString: { } };
} catch (e) {
    // 这里捕获到一个异常,因为尝试在作为 toString
    // 属性值的原生对象上调用 [[Call]]。
}

try {
    d.type = Symbol();
} catch (e) {
    // 这里捕获到一个异常,因为尝试在 Symbol 值上调用
    // JavaScript 的 ToString 抽象操作。
}

d.type = et;
// 这里抛出了一个未捕获的 "NotSupportedError" DOMException,因为
// [[DefaultValue]] 算法尝试获取 ExceptionThrower 对象上
// "valueOf" 属性的值。该异常会从这个代码块中传播出去。

4. 通用定义

本节规定了所有符合性实现必须支持的一些通用定义。

4.1. ArrayBufferView

typedef (Int8Array or Int16Array or Int32Array or
           Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
           BigInt64Array or BigUint64Array or
           Float16Array or Float32Array or Float64Array or DataView) ArrayBufferView;

ArrayBufferView 类型定义用于表示那些提供对 ArrayBufferSharedArrayBuffer(当使用 [AllowShared])进行视图的对象。

4.2. BufferSource

typedef (ArrayBufferView or ArrayBuffer) BufferSource;

BufferSource 类型定义用于表示那些本身是 ArrayBuffer 或提供对 ArrayBuffer 视图的对象。

注意: [AllowShared] 不能用于 BufferSource,因为 ArrayBuffer 不支持它。应该使用 AllowSharedBufferSource

4.3. AllowSharedBufferSource

typedef (ArrayBuffer or SharedArrayBuffer or [AllowShared] ArrayBufferView) AllowSharedBufferSource;

AllowSharedBufferSource 类型定义用于表示那些本身是 ArrayBufferSharedArrayBuffer,或者提供对 ArrayBufferSharedArrayBuffer 视图的对象。

4.4. DOMException

DOMException 类型是一个由以下 IDL 片段定义的接口类型

[Exposed=*,
 Serializable]
interface DOMException { // but see below note about JavaScript binding
  constructor(optional DOMString message = "", optional DOMString name = "Error");
  readonly attribute DOMString name;
  readonly attribute DOMString message;
  readonly attribute unsigned short code;

  const unsigned short INDEX_SIZE_ERR = 1;
  const unsigned short DOMSTRING_SIZE_ERR = 2;
  const unsigned short HIERARCHY_REQUEST_ERR = 3;
  const unsigned short WRONG_DOCUMENT_ERR = 4;
  const unsigned short INVALID_CHARACTER_ERR = 5;
  const unsigned short NO_DATA_ALLOWED_ERR = 6;
  const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7;
  const unsigned short NOT_FOUND_ERR = 8;
  const unsigned short NOT_SUPPORTED_ERR = 9;
  const unsigned short INUSE_ATTRIBUTE_ERR = 10;
  const unsigned short INVALID_STATE_ERR = 11;
  const unsigned short SYNTAX_ERR = 12;
  const unsigned short INVALID_MODIFICATION_ERR = 13;
  const unsigned short NAMESPACE_ERR = 14;
  const unsigned short INVALID_ACCESS_ERR = 15;
  const unsigned short VALIDATION_ERR = 16;
  const unsigned short TYPE_MISMATCH_ERR = 17;
  const unsigned short SECURITY_ERR = 18;
  const unsigned short NETWORK_ERR = 19;
  const unsigned short ABORT_ERR = 20;
  const unsigned short URL_MISMATCH_ERR = 21;
  const unsigned short QUOTA_EXCEEDED_ERR = 22;
  const unsigned short TIMEOUT_ERR = 23;
  const unsigned short INVALID_NODE_TYPE_ERR = 24;
  const unsigned short DATA_CLONE_ERR = 25;
};

注意:正如在 § 3.14.1 DOMException 自定义绑定 中讨论的那样,JavaScript 绑定对接口类型施加了超出常规要求的额外要求。

每个 DOMException 对象都有一个关联的名称消息, 两者都是字符串

new DOMException(message, name) 构造函数步骤如下:

  1. 对象的名称设置为 name

  2. 对象的消息设置为 message

name 获取器步骤是返回对象的名称

message 获取器步骤是返回对象的消息

code 获取器步骤是返回DOMException 名称表中为对象的名称指示的旧版代码,如果表中不存在此类条目,则返回 0。

DOMException 对象是可序列化对象

给定 valueserialized,它们的序列化步骤如下:

  1. serialized.[[Name]] 设置为 value名称
  2. serialized.[[Message]] 设置为 value消息
  3. 用户代理应将任何尚未指定的、值得关注的附带数据的序列化表示(特别是 stack 属性)附加到 serialized

给定 valueserialized,它们的反序列化步骤如下:

  1. value名称设置为 serialized.[[Name]]。
  2. value消息设置为 serialized.[[Message]]。
  3. 如果任何其他数据附加到 serialized,则反序列化并将其附加到 value

4.5. 函数

callback Function = any (any... arguments);

Function 回调函数类型用于表示没有对传递的参数或返回值类型做任何限制的函数值。

4.6. VoidFunction

callback VoidFunction = undefined ();

VoidFunction 回调函数类型用于表示不接受任何参数且不返回任何值的函数。

5. 扩展性

本节为说明性内容。

可以通过使用扩展属性来指定对语言绑定要求的扩展,这些扩展不能与本文件中定义的内容冲突。用于私有、项目特定用途的扩展不应包含在其他规范中出现的IDL 片段中。建议将需要在其他规范中使用的扩展与负责 Web IDL 工作的团队协调,该团队在撰写本文时为 W3C Web Platform 工作组,以便可能在本文件的未来版本中包含这些扩展。

强烈不建议对 IDL 语言的任何其他方面进行扩展。

6. 遗留构造

本节为说明性内容。

遗留的 Web IDL 构造仅存在于指定遗留 Web 平台特性时使用。它们通常以 "Legacy" 字符串为前缀。除非需要指定遗留 Web 平台特性的行为,或为了与这些特性保持一致,否则强烈不建议在规范中使用遗留的 Web IDL 构造。希望使用遗留 Web IDL 构造的编辑者应在继续之前通过提交问题进行讨论。

将构造标记为遗留构造并不意味着它即将从本规范中移除。然而,它确实表明它是未来从本规范中移除的良好候选项,尤其是当各种启发式方法表明它所帮助指定的 Web 平台特性可以完全移除,或可以修改为依赖于非遗留的 Web IDL 构造时。

7. 引用本规范

本节为说明性内容。

预计其他规范在定义 Web 平台接口时,使用一个或多个IDL 片段将引用本规范。建议这些规范包括以下句子,以表明 IDL 应按照本规范的描述进行解释:

本规范附录 A 中的 IDL 片段,结合本规范规范性参考中定义的 IDL 片段,应按“Web IDL”规范中所述要求进行解释,以确保符合的 IDL 片段集。[WEBIDL]

此外,建议引用规范中的用户代理一致性类链接到本规范中的符合实现类:

符合要求的 FooML 用户代理还必须是本规范附录 A 中 IDL 片段的符合实现,如“Web IDL”规范中所述。[WEBIDL]

8. 隐私和安全注意事项

本规范定义了 JavaScript 与 IDL 值之间的转换层。不正确地实现这一层可能会导致安全问题。

本规范还通过anyobject IDL 类型提供了直接使用 JavaScript 值的能力。这些值需要谨慎处理以避免安全问题。特别是,用户脚本可以响应对这些值的几乎任何操作运行,并使使用它们的规范或实现的预期失效。

本规范使得与SharedArrayBuffer对象的交互成为可能,这些对象可以被用来构建时间攻击。使用这些对象的规范需要考虑此类攻击。

致谢

本节为说明性内容。

编辑们感谢以下人士为本规范做出的贡献: Glenn Adams, David Andersson, Jake Archibald, Tab Atkins-Bittner, L. David Baron, Art Barstow, Nils Barth, Robin Berjon, David Bruant, Jan-Ivar Bruaroey, Marcos Cáceres, Giovanni Campagna, François Daoust, Domenic Denicola, Chris Dumez, Michael Dyck, Daniel Ehrenberg, Brendan Eich, João Eiras, Gorm Haug Eriksen, Sigbjorn Finne, David Flanagan, Aryeh Gregor, Dimitry Golubovsky, James Graham, Aryeh Gregor, Tiancheng “Timothy” Gu, Kartikaya Gupta, Marcin Hanclik, Jed Hartman, Stefan Haustein, Dominique Hazaël-Massieux, Ian Hickson, Björn Höhrmann, Kyle Huey, Lachlan Hunt, Oliver Hunt, Jim Jewett, Wolfgang Keller, Anne van Kesteren, Olav Junker Kjær, Takayoshi Kochi, Magnus Kristiansen, Raphael Kubo da Costa, Takeshi Kurosawa, Yves Lafon, Travis Leithead, Jim Ley, Kevin Lindsey, Jens Lindström, Peter Linss, 吕康豪 (Kang-Hao Lu), Kyle Machulis, Darien Maillet Valentine, Mark Miller, Ms2ger, Andrew Oakley, 岡坂 史紀 (Shiki Okasaka), Jason Orendorff, Olli Pettay, Simon Pieters, Andrei Popescu, François Remy, Tim Renouf, Jeremy Roman, Tim Ruffles, Alex Russell, Takashi Sakamoto, Doug Schepers, Jonas Sicking, Garrett Smith, Sam Sneddon, Jungkee Song, Josh Soref, Maciej Stachowiak, Austin Sullivan, Anton Tayanovskyy, triple-underscore, Peter Van der Beken, Jeff Walden, Allen Wirfs-Brock, Jeffrey Yasskin, 以及 Collin Xu。

特别感谢 Sam Weinig 在编辑无法完成此文档期间进行维护。

本标准由 Edgar Chen (Mozilla, echen@mozilla.com) 和 Tiancheng "Timothy" Gu (timothygu99@gmail.com) 编写,Boris Zbarsky (bzbarsky@mit.edu), Cameron McCormack (cam@mcc.id.au) 以及 Tobie Langel (tobie@unlockopen.com) 也做出了重要贡献。

IDL 语法

本节定义了一个 LL(1) 语法,其起始符号 Definitions 匹配整个 IDL 片段

语法中的每个生成式在其右侧要么有一个非零序列的终端符号和非终端符号,要么是表示无符号的 epsilon (ε)。以大写字母开头的符号是非终端符号。以等宽字体显示的符号是终端符号。以无衬线字体显示且以小写字母开头的符号是符合正则表达式(使用 Perl 5 正则表达式语法 [PERLRE])的终端符号:

integer = /-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/
decimal = /-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/
identifier = /[_-]?[A-Za-z][0-9A-Z_a-z-]*/
string = /"[^"]*"/
whitespace = /[\t\n\r ]+/
comment = /\/\/.*|\/\*(.|\n)*?\*\//
other = /[^\t\n\r 0-9A-Za-z]/

词法分析器作用于 标量值的序列。当进行词法分析时,必须使用尽可能长的匹配。例如,如果输入文本为 “a1”,则其被词法分析为一个 identifier,而不是一个单独的 identifierinteger。如果最长可能的匹配既可以匹配上述命名的终端符号,也可以匹配语法中的其他终端符号,则必须将其词法分析为后者。因此,输入文本 “long” 被词法分析为终端符号 long 而不是 identifier,名为 "long",输入文本 “.” 被词法分析为终端符号 . 而不是 other

IDL 语法对在语法中使用的等宽字体终端符号以及用于 identifier 终端的值均区分大小写。因此,例如,输入文本 “Const” 被词法分析为 identifier 而不是终端符号 const,一个 接口标识符 "A" 与名为 "a" 的标识符是不同的,一个 [legacyfactoryfunction] 扩展属性不会被识别为 [LegacyFactoryFunction] 扩展属性。

隐式地,在被解析的输入文本中的每个其他终端符号之间都允许有任意数量的 whitespacecomment 终端符号。在解析时,这些 whitespacecomment 终端符号会被忽略。

以下 LL(1) 语法,从 Definitions 开始,匹配一个 IDL 片段

Definitions ::
      ExtendedAttributeList Definition Definitions
      ε
  
  Definition ::
      CallbackOrInterfaceOrMixin
      Namespace
      Partial
      Dictionary
      Enum
      Typedef
      IncludesStatement
  
  ArgumentNameKeyword ::
      async
      attribute
      callback
      const
      constructor
      deleter
      dictionary
      enum
      getter
      includes
      inherit
      interface
      iterable
      maplike
      mixin
      namespace
      partial
      readonly
      required
      setlike
      setter
      static
      stringifier
      typedef
      unrestricted
  
  CallbackOrInterfaceOrMixin ::
      callback CallbackRestOrInterface
      interface InterfaceOrMixin
  
  InterfaceOrMixin ::
      InterfaceRest
      MixinRest
  
  InterfaceRest ::
      identifier Inheritance { InterfaceMembers } ;
  
  Partial ::
      partial PartialDefinition
  
  PartialDefinition ::
      interface PartialInterfaceOrPartialMixin
      PartialDictionary
      Namespace
  
  PartialInterfaceOrPartialMixin ::
      PartialInterfaceRest
      MixinRest
  
  PartialInterfaceRest ::
      identifier { PartialInterfaceMembers } ;
  
  InterfaceMembers ::
      ExtendedAttributeList InterfaceMember InterfaceMembers
      ε
  
  InterfaceMember ::
      PartialInterfaceMember
      Constructor
  
  PartialInterfaceMembers ::
      ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers
      ε
  
  PartialInterfaceMember ::
      Const
      Operation
      Stringifier
      StaticMember
      Iterable
      AsyncIterable
      ReadOnlyMember
      ReadWriteAttribute
      ReadWriteMaplike
      ReadWriteSetlike
      InheritAttribute
  
  Inheritance ::
      : identifier
      ε
  
  MixinRest ::
      mixin identifier { MixinMembers } ;
  
  MixinMembers ::
      ExtendedAttributeList MixinMember MixinMembers
      ε
  
  MixinMember ::
      Const
      RegularOperation
      Stringifier
      OptionalReadOnly AttributeRest
  
  IncludesStatement ::
      identifier includes identifier ;
  
  CallbackRestOrInterface ::
      CallbackRest
      interface identifier { CallbackInterfaceMembers } ;
  
  CallbackInterfaceMembers ::
      ExtendedAttributeList CallbackInterfaceMember CallbackInterfaceMembers
      ε
  
  CallbackInterfaceMember ::
      Const
      RegularOperation
  
  Const ::
      const ConstType identifier = ConstValue ;
  
  ConstValue ::
      BooleanLiteral
      FloatLiteral
      integer
  
  BooleanLiteral ::
      true
      false
  
  FloatLiteral ::
      decimal
      -Infinity
      Infinity
      NaN
  
  ConstType ::
      PrimitiveType
      identifier
  
  ReadOnlyMember ::
      readonly ReadOnlyMemberRest
  
  ReadOnlyMemberRest ::
      AttributeRest
      MaplikeRest
      SetlikeRest
  
  ReadWriteAttribute ::
      AttributeRest
  
  InheritAttribute ::
      inherit AttributeRest
  
  AttributeRest ::
      attribute TypeWithExtendedAttributes AttributeName ;
  
  AttributeName ::
      AttributeNameKeyword
      identifier
  
  AttributeNameKeyword ::
      async
      required
  
  OptionalReadOnly ::
      readonly
      ε
  
  DefaultValue ::
      ConstValue
      string
      [ ]
      { }
      null
      undefined
  
  Operation ::
      RegularOperation
      SpecialOperation
  
  RegularOperation ::
      Type OperationRest
  
  SpecialOperation ::
      Special RegularOperation
  
  Special ::
      getter
      setter
      deleter
  
  OperationRest ::
      OptionalOperationName ( ArgumentList ) ;
  
  OptionalOperationName ::
      OperationName
      ε
  
  OperationName ::
      OperationNameKeyword
      identifier
  
  OperationNameKeyword ::
      includes
  
  ArgumentList ::
      Argument Arguments
      ε
  
  Arguments ::
      , Argument Arguments
      ε
  
  Argument ::
      ExtendedAttributeList ArgumentRest
  
  ArgumentRest ::
      optional TypeWithExtendedAttributes ArgumentName Default
      Type Ellipsis ArgumentName
  
  ArgumentName ::
      ArgumentNameKeyword
      identifier
  
  Ellipsis ::
      ...
      ε
  
  Constructor ::
      constructor ( ArgumentList ) ;
  
  Stringifier ::
      stringifier StringifierRest
  
  StringifierRest ::
      OptionalReadOnly AttributeRest
      ;
  
  StaticMember ::
      static StaticMemberRest
  
  StaticMemberRest ::
      OptionalReadOnly AttributeRest
      RegularOperation
  
  Iterable ::
      iterable < TypeWithExtendedAttributes OptionalType > ;
  
  OptionalType ::
      , TypeWithExtendedAttributes
      ε
  
  AsyncIterable ::
      async iterable < TypeWithExtendedAttributes OptionalType > OptionalArgumentList ;
  
  OptionalArgumentList ::
      ( ArgumentList )
      ε
  
  ReadWriteMaplike ::
      MaplikeRest
  
  MaplikeRest ::
      maplike < TypeWithExtendedAttributes , TypeWithExtendedAttributes > ;
  
  ReadWriteSetlike ::
      SetlikeRest
  
  SetlikeRest ::
      setlike < TypeWithExtendedAttributes > ;
  
  Namespace ::
      namespace identifier { NamespaceMembers } ;
  
  NamespaceMembers ::
      ExtendedAttributeList NamespaceMember NamespaceMembers
      ε
  
  NamespaceMember ::
      RegularOperation
      readonly AttributeRest
      Const
  
  Dictionary ::
      dictionary identifier Inheritance { DictionaryMembers } ;
  
  DictionaryMembers ::
      DictionaryMember DictionaryMembers
      ε
  
  DictionaryMember ::
      ExtendedAttributeList DictionaryMemberRest
  
  DictionaryMemberRest ::
      required TypeWithExtendedAttributes identifier ;
      Type identifier Default ;
  
  PartialDictionary ::
      dictionary identifier { DictionaryMembers } ;
  
  Default ::
      = DefaultValue
      ε
  
  Enum ::
      enum identifier { EnumValueList } ;
  
  EnumValueList ::
      string EnumValueListComma
  
  EnumValueListComma ::
      , EnumValueListString
      ε
  
  EnumValueListString ::
      string EnumValueListComma
      ε
  
  CallbackRest ::
      identifier = Type ( ArgumentList ) ;
  
  Typedef ::
      typedef TypeWithExtendedAttributes identifier ;
  
  Type ::
      SingleType
      UnionType Null
  
  TypeWithExtendedAttributes ::
      ExtendedAttributeList Type
  
  SingleType ::
      DistinguishableType
      any
      PromiseType
  
  UnionType ::
      ( UnionMemberType or UnionMemberType UnionMemberTypes )
  
  UnionMemberType ::
      ExtendedAttributeList DistinguishableType
      UnionType Null
  
  UnionMemberTypes ::
      or UnionMemberType UnionMemberTypes
      ε
  
  DistinguishableType ::
    PrimitiveType Null
    StringType Null
    identifier Null
    sequence < TypeWithExtendedAttributes > Null
    async iterable < TypeWithExtendedAttributes > Null
    object Null
    symbol Null
    BufferRelatedType Null
    FrozenArray < TypeWithExtendedAttributes > Null
    ObservableArray < TypeWithExtendedAttributes > Null
    RecordType Null
    undefined Null

  PrimitiveType ::
      UnsignedIntegerType
      UnrestrictedFloatType
      boolean
      byte
      octet
      bigint
  
  UnrestrictedFloatType ::
      unrestricted FloatType
      FloatType
  
  FloatType ::
      float
      double
  
  UnsignedIntegerType ::
      unsigned IntegerType
      IntegerType
  
  IntegerType ::
      short
      long OptionalLong
  
  OptionalLong ::
      long
      ε
  
  StringType ::
      ByteString
      DOMString
      USVString
  
  PromiseType ::
      Promise < Type >
  
  RecordType ::
      record < StringType , TypeWithExtendedAttributes >
  
  Null ::
      ?
      ε
  
  BufferRelatedType ::
      ArrayBuffer
      SharedArrayBuffer
      DataView
      Int8Array
      Int16Array
      Int32Array
      Uint8Array
      Uint16Array
      Uint32Array
      Uint8ClampedArray
      BigInt64Array
      BigUint64Array
      Float16Array
      Float32Array
      Float64Array
  
  ExtendedAttributeList ::
      [ ExtendedAttribute ExtendedAttributes ]
      ε
  
  ExtendedAttributes ::
      , ExtendedAttribute ExtendedAttributes
      ε
  
  ExtendedAttribute ::
      ( ExtendedAttributeInner ) ExtendedAttributeRest
      [ ExtendedAttributeInner ] ExtendedAttributeRest
      { ExtendedAttributeInner } ExtendedAttributeRest
      Other ExtendedAttributeRest
  
  ExtendedAttributeRest ::
      ExtendedAttribute
      ε
  
  ExtendedAttributeInner ::
      ( ExtendedAttributeInner ) ExtendedAttributeInner
      [ ExtendedAttributeInner ] ExtendedAttributeInner
      { ExtendedAttributeInner } ExtendedAttributeInner
      OtherOrComma ExtendedAttributeInner
      ε
  
  Other ::
      integer
      decimal
      identifier
      string
      other
      -
      -Infinity
      .
      ...
      :
      ;
      <
      =
      >
      ?
      *
      ByteString
      DOMString
      FrozenArray
      Infinity
      NaN
      ObservableArray
      Promise
      USVString
      any
      bigint
      boolean
      byte
      double
      false
      float
      long
      null
      object
      octet
      or
      optional
      record
      sequence
      short
      symbol
      true
      unsigned
      undefined
      ArgumentNameKeyword
      BufferRelatedType
  
  OtherOrComma ::
      Other
      ,
  
  IdentifierList ::
      identifier Identifiers
  
  Identifiers ::
      , identifier Identifiers
      ε
  
  ExtendedAttributeNoArgs ::
      identifier
  
  ExtendedAttributeArgList ::
      identifier ( ArgumentList )
  
  ExtendedAttributeIdent ::
      identifier = identifier
  
  ExtendedAttributeWildcard ::
      identifier = *
  
  ExtendedAttributeIdentList ::
      identifier = ( IdentifierList )
  
  ExtendedAttributeNamedArgList ::
      identifier = identifier ( ArgumentList )
  
  

注意: Other 非终端匹配除了 ()[]{}, 之外的任何单个终端符号。

虽然 ExtendedAttribute 非终端匹配任意非空的终端符号序列(只要任何括号、中括号或大括号是平衡的,且 , 只出现在这些平衡的括号中),但本规范定义的 扩展属性 仅使用了这些可能序列的一个子集——详见 § 2.14 扩展属性,了解这些扩展属性使用的语法。

文档约定

本文档中使用了以下排版约定:

本文档中的算法使用了以下约定:

一致性

本规范中的所有内容均为规范性,除图表、示例、注释和标注为信息性部分的章节外。

本规范依赖于 Infra 标准。[INFRA]

本规范定义了以下一致性类别:

符合要求的 IDL 片段集合

当一组 IDL 片段 共同满足本规范中对 IDL 片段适用的所有必须、必需和应级别的标准时,认为其为 符合要求的 IDL 片段集合

符合要求的实现

当用户代理相对于 符合要求的 IDL 片段集合,满足本规范中对用户代理所支持的所有语言绑定的实现适用的所有必须、必需和应级别的标准时,认为其为 符合要求的实现

一致的 JavaScript 实现

当用户代理相对于 符合要求的 IDL 片段集合,满足本规范中对 JavaScript 语言绑定的实现适用的所有必须、必需和应级别的标准时,认为其为 符合要求的 JavaScript 实现

知识产权

本现行标准包含从 W3C 的 WebIDL [sic] Level 1 复制的内容,遵循 W3C 软件和文档许可

版权 © WHATWG(Apple、Google、Mozilla、Microsoft)。本作品根据 知识共享署名 4.0 国际许可协议 许可。若将本标准的部分内容并入源代码中,这些部分在源代码中将根据 BSD 3-Clause License 许可。

这是现行标准。有兴趣查看专利审核版本的人应参阅 现行标准审核草案

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性参考文献

[DOM]
Anne van Kesteren. DOM 标准。现行标准。URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范。URL: https://tc39.es/ecma262/multipage/
[ECMA-402]
ECMAScript 国际化 API 规范。URL: https://tc39.es/ecma402/
[HTML]
Anne van Kesteren; et al. HTML 标准。现行标准。URL: https://html.spec.whatwg.org/multipage/
[IEEE-754]
IEEE 浮点运算标准。2019年7月22日。URL:https://ieeexplore.ieee.org/document/8766229
[Infra]
Anne van Kesteren; Domenic Denicola. Infra 标准。现行标准。URL: https://infra.spec.whatwg.org/
[PERLRE]
Perl 正则表达式 (Perl 5.8.8)。2006年2月。URL: http://search.cpan.org/dist/perl/pod/perlre.pod
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。现行标准。URL: https://webidl.spec.whatwg.org/

信息性参考文献

[API-DESIGN-PRINCIPLES]
Martin Thomson;Jeffrey Yasskin。 Web 平台设计原则。 URL:https://w3ctag.github.io/design-principles/
[CSS3-CONDITIONAL]
Chris Lilley; David Baron; Elika Etemad. CSS 条件规则模块 第 3 级。URL: https://drafts.csswg.org/css-conditional-3/
[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS 对象模型 (CSSOM)。URL: https://drafts.csswg.org/cssom/
[FETCH]
Anne van Kesteren. Fetch 标准。现行标准。URL: https://fetch.spec.whatwg.org/
[INDEXEDDB]
Nikunj Mehta; et al. 索引数据库 API。URL: https://w3c.github.io/IndexedDB/
[PROPOSAL-FLOAT16ARRAY]
在 JavaScript 中添加 float16 类型数组的提案。URL: https://tc39.es/proposal-float16array/
[SERVICE-WORKERS]
Yoshisato Yanagisawa;Monica CHINTALA。 Service Workers。 URL:https://w3c.github.io/ServiceWorker/
[STREAMS]
Adam Rice; et al. Streams 标准。现行标准。URL: https://streams.spec.whatwg.org/
[WASM-JS-API-1]
Daniel Ehrenberg. WebAssembly JavaScript 接口。URL: https://webassembly.github.io/spec/js-api/
[WASM-JS-API-2]
Ms2ger. WebAssembly JavaScript 接口。URL: https://webassembly.github.io/spec/js-api/
[XML-NAMES]
Tim Bray; et al. XML 名空间 1.0 (第三版)。2009年12月8日。REC。URL: https://www.w3.org/TR/xml-names/

IDL 索引

typedef (Int8Array or Int16Array or Int32Array or
           Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
           BigInt64Array or BigUint64Array or
           Float16Array or Float32Array or Float64Array or DataView) ArrayBufferView;
  
  typedef (ArrayBufferView or ArrayBuffer) BufferSource;
  typedef (ArrayBuffer or SharedArrayBuffer or [AllowShared] ArrayBufferView) AllowSharedBufferSource;
  [Exposed=*,
   Serializable]
  interface DOMException { // but see below note about JavaScript binding
    constructor(optional DOMString message = "", optional DOMString name = "Error");
    readonly attribute DOMString name;
    readonly attribute DOMString message;
    readonly attribute unsigned short code;
  
    const unsigned short INDEX_SIZE_ERR = 1;
    const unsigned short DOMSTRING_SIZE_ERR = 2;
    const unsigned short HIERARCHY_REQUEST_ERR = 3;
    const unsigned short WRONG_DOCUMENT_ERR = 4;
    const unsigned short INVALID_CHARACTER_ERR = 5;
    const unsigned short NO_DATA_ALLOWED_ERR = 6;
    const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7;
    const unsigned short NOT_FOUND_ERR = 8;
    const unsigned short NOT_SUPPORTED_ERR = 9;
    const unsigned short INUSE_ATTRIBUTE_ERR = 10;
    const unsigned short INVALID_STATE_ERR = 11;
    const unsigned short SYNTAX_ERR = 12;
    const unsigned short INVALID_MODIFICATION_ERR = 13;
    const unsigned short NAMESPACE_ERR = 14;
    const unsigned short INVALID_ACCESS_ERR = 15;
    const unsigned short VALIDATION_ERR = 16;
    const unsigned short TYPE_MISMATCH_ERR = 17;
    const unsigned short SECURITY_ERR = 18;
    const unsigned short NETWORK_ERR = 19;
    const unsigned short ABORT_ERR = 20;
    const unsigned short URL_MISMATCH_ERR = 21;
    const unsigned short QUOTA_EXCEEDED_ERR = 22;
    const unsigned short TIMEOUT_ERR = 23;
    const unsigned short INVALID_NODE_TYPE_ERR = 24;
    const unsigned short DATA_CLONE_ERR = 25;
  };
  
  callback Function = any (any... arguments);
  callback VoidFunction = undefined ();
  
MDN

DOMException/DOMException

In all current engines.

Firefox37+Safari10.1+Chrome46+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

DOMException/message

In all current engines.

Firefox1+Safari1+Chrome1+
Opera12.1+Edge79+
Edge (Legacy)12+IE9+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

DOMException/name

In all current engines.

Firefox1+Safari1+Chrome1+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

DOMException

In all current engines.

Firefox1+Safari1+Chrome1+
Opera12.1+Edge79+
Edge (Legacy)12+IE9+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
Node.js17.0.0+