ES5/类型

From HTML5 Chinese Interest Group Wiki
< ES5(Redirected from ES5/types)
Jump to: navigation, search

本规范的算法会处理每个关联了类型的值,所有可能出现的值的类型都定义在本条目中。这些类型又再细分为 ECMAScript语言类型规范类型

ECMAScript语言类型 对应的是程序员使用 ECMAScript 语言直接操作的值。ECMAScript语言类型 包括 UndefinedNullBooleanStringNumberObject

规范类型 对应的是 元值,它被用于在算法中描述 ECMAScript 语言结构的语义和 ECMAScript语言类型规范类型包括引用列表完结属性描述式属性标示词法环境 环境纪录规范类型的值是规范自造的,在 ECMAScript 的实现中不必对应到特定的实体上。规范类型可用来描述 ECMAScript 表达式运算的中间结果,但这样的值不能储存为对象的属性或 ECMAScript 语言的变量值。

在本规范中,记法 Type(x) 表示 “x 的类型”,而“类型”指的就是上述的 ECMAScript语言类型规范类型

Undefined类型

Undefined类型 有且只有一个值,称为 undefined 。任何没有被赋值的变量值为 undefined


Null类型

Null类型 有且只有一个值,称为 null


Boolean类型

Boolean类型 表示逻辑实体,有两个值,称为 truefalse


String类型

字符串类型是所有有限的零个或多个16位无符号整数值(“元素”)的有序序列。在运行的 ECMAScript 程序中,字符串类型常被用于表示文本数据,此时字符串中的每个元素都被视为一个 代码单元(参看 章节 6)。 每个元素都被认为占有此序列中的一个位置,用非负整数索引这些位置。任何时候,第一个元素(若存在)在位置0,下一个元素(若存在)在位置1,依此类推。字符串的长度即其中元素(即16位的值)的个数。空字符串长度为零,因而不包含任何元素

若一个字符串包含实际的文本数据,每个元素都被认为是一个单独的 UTF-16 代码单元。无论这是不是 String 实际的存储格式,String 中的字符都被当作表示为 UTF-16 来计数。除非特别声明,作用在字符串上的所有操作都视它们为无差别的 16 位无符号整数;这些操作不保证结果字符串仍为正规形式,也不保证语言敏感结果。

注: 这些决议背后的原理是尽可能地保持字符串的实现简单而高效。这意味着,在运行中的程序读到从外部进入执行环境的文本数据(即,用户输入、从文件读取文本 、从网络上接收文本,等等)之前,它们已被转为 Unicode 正规形式 C。通常情况下,这个转化在进入的文本被从其原始字符编码转为 Unicode 的同时进行(且强制去除头部附加信息)。因此,建议 ECMAScript 程序源代码为正规形式 C,保证字符串常量是正规化的(如果保证源代码文本是正规化的话),即便它们不包含任何 Unicode 转义序列。

Number 类型

精确地描述,数值类型拥有 18437736874454810627(即,264-253+3)个值,表示为 IEEE-754 格式 64 位双精度数值(IEEE 二进制浮点数算术中描述了它),除了 IEEE 标准中的 9007199254740990(即,253-2)个明显的“非数字”值;在 ECMAScript 中,它们被表示为一个单独的特殊值:NaN。(请注意,NaN 值由程序表达式 NaN 产生,并假设执行程序不能调整定义的全局变量 NaN。) 在某些实现中,外部代码也许有能力探测出众多非数字值之间的不同,但此类行为依赖于具体实现;对于 ECMAScript 代码而言,NaN 值相互之间无法区别。

还有另外两个特殊值,称为正无穷和负无穷。为简洁起见,在说明目的时,用符号 +∞-∞ 分别代表它们。(请注意,两个无限数值由程序表达式 +Infinity(简作 Infinity) 和 -Infinity 产生,并假设执行程序不能调整定义的全局变量 Infinity。)

另外 18437736874454810624(即,264-253) 个值被称为有限数值。其中的一半是正数,另一半是负数,对于每个正数而言,都有一个与之对应的、相同规模的负数。

请注意,还有一个正零和一个负零。为简洁起见,类似地,在说明目的时,分别用用符号 +0-0 代表这些值。(请注意,这两个数字零由程序表达式 +0(简作 0) 和 -0 产生。)

18437736874454810622(即,264-253-2) 个有限非零值分为两种:

其中 18428729675200069632(即,264-254) 个是常规值,形如

   s×m×2e

这里的 s+1-1m 是一个小于 253 但不小于 252 的正整数,m 是一个闭区间 -1074971 中的整数。

剩下的 9007199254740990(即,253-2)个值是非常规的,形如

   s×m×2e

这里的 m+1-1m 是一个小于 252 的 正整数,e-1074

请注意,所有规模不超过 253 的正整数和负整数都可被数值类型表示(不过,整数 0 有两个呈现形式,+00)。

如果一个有限的数值非零且用来表达它(上文两种形式之一)的整数 m 是奇数,则该数值有 奇数标记。否则,它有 偶数标记

在本规范中,当 x 表示一个精确的非零实数数学量(甚至可以是无理数,比如 π)时,短语 "x 的 Number 值" 意为,以下面的方式选择一个 Number 值。考虑数值类型的所有有限值的集合(不包括 -0 和两个被加入在数值类型中但不可呈现的值,即 21024(即 +1×253×2971)和 -21024 (即 -1×253×2971)。选择此集合 中值最接近 x 的一员,若集合中的两值近似相等,那么选择有偶数标记的那个;为此,21024-21024 这两个超额值被认为有偶数标记。最终,若选择 21024 ,用 +∞ 替换它;若选择 -21024 ,用 -∞ 替换它;若选择 +0,有且只有 x 小于零时,用 -0 替换它;其它任何被选取的值都不用改变。结果就是 x 的 Number 值。(此过程正是 IEEE-754 的四舍五入模式对应的行为。)

某些 ECMAScript 运算符仅涉及闭区间 -231231-1 的整数,或闭区间 0232-1。这些运算符接受任何数值类型的值,不过,数值首先被转换为 232 个整数值中的一个。参见 ToInt32ToUint32 的描述,分别在章节 9.59.6 中。

Object 类型

Object 是一个属性的集合。每个属性既可以是一个命名数据属性,也可以是一个命名访问器属性,或是一个内部属性

  • 命名数据属性 由一个名字与一个 ECMAScript语言类型 值和一个 Boolean 属性集合组成。
  • 命名访问器属性 由一个名字与一个或两个访问器函数,和一个 Boolean 属性集合组成。访问器函数用于存取一个与该属性相关联的 ECMAScript语言类型 值。
  • 内部属性 没有名字,且不能直接通过 ECMAScript 语言操作。内部属性的存在纯粹为了规范的目的。

有两种带名字的访问器属性(非内部属性):getput,分别对应取值和赋值。

属性的特性

本规范中的特性用于定义和解释命名属性的状态。命名的数据属性由一个名字关联到一个下表中列出的特性

表5:命名的数据属性的特性
特性名称 取值范围 描述
[[Value]] 任何 ECMAScript语言类型 通过读属性来取到该值
[[Writable]] Boolean 如果为 false,试图通过 ECMAScript 代码 [[Put]] 去改变该属性的 [[Value]],将会失败
[[Enumerable]] Boolean 如果为 true,则该属性可被 for-in 枚举出来,否则,该属性不可枚举。
[[Configurable]] Boolean 如果为 false,试图删除该属性或改变该属性为访问器属性,或改变它的特性(除了 [[Value]]),都会失败。


命名访问器属性由一个名字关联到一个下表中列出的特性

表6:命名的访问器属性的特性
特性名称 取值范围 描述
[[Get]] ObjectUndefined 如果该值为一个 Object 对象,那么它必须是一个函数对象。每次有对属性进行 get 访问时,该函数的内部方法 [[Call]] 会伴随着一个空参数列表被调用,并返回该属性值。
[[Set]] ObjectUndefined 如果该值为一个 Object 对象,那么它必须是一个函数对象。每次有对属性进行 set 访问时,该函数的内部方法 [[Call]] 会伴随着一个参数列表被调用,这个参数列表包含了指定值作为唯一的参数。属性的内部方法 [[Set]] 可能会对随后的 属性 内部方法 [[Get]] 的调用返回结果产生影响。
[[Enumerable]] Boolean 如果为 true,则该属性可被 for-in 枚举出来,否则,该属性不可枚举。
[[Configurable]] Boolean 如果为 false,试图删除该属性,改变该属性为数据属性,或改变它的特性,都会失败。

如果在此规范中没有对某个命名属性的特性做出明确指定,那么它的默认值将使用下表的定义。

表7:默认的特性
名称 默认值
[[Value]] undefined
[[Get]] undefined
[[Set]] undefined
[[Writable]] false
[[Enumerable]] false
[[Configurable]] false

Object的内部属性及方法

本规范使用各种内部属性来定义对象值的语义。这些内部属性不是 ECMAScript 语言的一部分。本规范中纯粹是以说明为目的定义它们。ECMAScript 实现需要保持和这里描述的内部属性产生和操作的结果一致。内部属性的名字用闭合双方括号“[[ ]]”括起来。如果一个算法使用一个对象的一个内部属性,而此对象并没有实现需要的内部属性,那么就抛出 TypeError 异常。

表8 总结了本规范中适用于所有 ECMAScript 对象的内部属性。表9 总结了本规范中适用于某些 ECMAScript 对象的内部属性。这些表中的描述如果没有特别指出是特定的原生 ECMAScript 对象,那么就说明了其在原生 ECMAScript 对象中的行为。宿主对象的内部属性可以支持任何依赖于实现的行为,只要其与本文档说的宿主对象的个别限制一致。

下面表的 “值的类域” 一列定义了内部属性关联值的类型。类型名称参考第 8 章定义的类型,作为增强添加了一下名称:“任意” 指值可以是任何 ECMAScript语言类型原始类型UndefinedNullBooleanStringNumberSpecOp 指内部属性是一个内部方法,一个抽象操作规范定义一个实现提供它的步骤。“SpecOp” 后面跟着描述性参数名的列表。如果参数名和类型名一致那么这个名字用于描述参数的类型。如果 “SpecOp” 有返回值,那么这个参数列表后跟着 “→” 符号和返回值的类型。

表8 - 所有对象共有的内部属性
内部属性 值的类型范围 说明
[[Prototype]] ObjectNull 此对象的原型
[[Class]] String 说明规范定义的对象分类的一个字符串值
[[Extensible]] Boolean 如果是 true,可以向对象添加自身属性。
[[Get]] SpecOp(属性名) → 任意 返回命名属性的值
[[GetOwnProperty]] SpecOp(属性名) → Undefined属性描述 返回此对象的自身命名属性的属性描述,如果不存在返回 undefined
[[GetProperty]] SpecOp(属性名) → Undefined属性描述 返回此对象的完全填入的自身命名属性的属性描述,如果不存在返回 undefined
[[Put]] SpecOp(属性名, 任意, Boolean) 将指定命名属性设为第二个参数的值。flag 控制失败处理。
[[CanPut]] SpecOp(属性名) → Boolean 返回一个 Boolean 值,说明是否可以在 属性名 上执行 [[Put]] 操作。
[[HasProperty]] SpecOp (属性名) → Boolean 返回一个 Boolean 值,说明对象是否含有给定名称的属性。
[[Delete]] SpecOp(属性名, Boolean) → Boolean 从对象上删除指定的自身命名属性。flag 控制失败处理。
[[DefaultValue]] SpecOp(暗示) → 原始类型 暗示 是一个字符串 Question.png。返回对象的默认值
[[DefineOwnProperty]] SpecOp(属性名, 属性描述, Boolean) → Boolean 创建或修改自身命名属性为拥有属性描述里描述的状态。flag 控制失败处理。

所有对象(包括宿主对象)必须实现 表8 中列出的所有内部属性。然而,对某些对象的 [[DefaultValue]] 内部方法,可以简单的抛出 TypeError 异常。

所有对象都有一个叫做 [[Prototype]] 的内部属性。此对象的值是 null 或一个对象,并且它用于实现继承。一个原生属性是否可以把宿主对象作为它的 [[Prototype]] 取决于实现。所有 [[Prototype]] 链必须是有限长度(即,从任何对象开始,递归访问 [[Prototype]] 内部属性必须最终到头,并且值是 null)。从 [[Prototype]] 对象继承来的命名数据属性(作为子对象的属性可见)可以通过 get 获取,但无法用于 通过 put 写入。命名访问器属性会把 getput 请求都继承。

所有 ECMAScript对象 都有一个 Boolean 类型的 [[Extensible]] 内部属性,它决定了 是否可以给对象添加命名属性。如果 [[Extensible]] 内部属性的值是 false 就不得给对象添加命名属性。此外,如果 [[Extensible]]false 那么不得更改对象的 [[Class]][[Prototype]] 内部属性的值。一旦 [[Extensible]] 内部属性的值设为 false 之后无法再更改为 true

注: 本规范的定义中没有 ECMAScript 语言运算符或内置函数允许一个程序更改对象的 [[Class]] 或 [[Prototype]] 内部属性或把 [[Extensible]] 的值从 false 更改成 true。实现中修改 [[Class]], [[Prototype]], [[Extensible]] 的个别扩展必须不违反前一段定义的不变量。

本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 "Arguments""Array""Boolean""Date""Error""Function""JSON""Math""Number""Object""RegExp""String" 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。注,本规范中除了通过 Object.prototype.toString 没有提供任何手段使程序访问此值。

除非特别指出,原生 ECMAScrpit 对象的公共内部方法的行为描述在 8.12Array 对象的 [[DefineOwnProperty]] 内部方法有稍不同的实现,又有 String 对象的 [[GetOwnProperty]] 内部方法有稍不同的实现。Arguments 对象的 [[Get]][[GetOwnProperty]][[DefineOwnProperty]][[Delete]] 有不同的实现。Function 对象的 [[Get]] 有不同的实现。

除非特别指出,宿主对象可以以任何方式实现这些内部方法,一种可能是一个特别的宿主对象的 [[Get]][[Put]] 确实可以存取属性值,但 [[HasProperty]] 总是产生 false。然而,如果任何对宿主对象内部属性的操作不被实现支持,那么当试图操作时必须抛出 TypeError 异常。

宿主对象的 [[GetOwnProperty]] 内部方法必须符合宿主对象每个属性的以下不变量 :

  • 如果属性被作为数据属性来描述,并随着时间的推移,它可能返回不同的值,那么即使没有暴露提供更改值机制的其他内部方法,[[Writable]][[Configurable]] 之一或全部必须是 true
  • 如果属性被作为数据属性来描述,并且其 [[Writable]][[Configurable]] 都是 false。那么所有对 [[GetOwnProperty]] 的调用,必须返回作为属性 [[Value]] 特性的 SameValue
  • 如果 [[Writable]] 特性可以从 false 更改为 true,那么 [[Configurable]] 特性必须是 true
  • 当 ECMAScript 代码监测到宿主对象的 [[Extensible]] 内部属性值是 false。那么如果调用 [[GetOwnProperty]] 描述一个属性是不存在,那么接下来所有调用这个属性必须也描述为不存在。

如果 ECMAScript 代码监测到宿主对象的 [[Extensible]] 内部属性是 false,那么这个宿主对象的 [[DefineOwnProperty]] 内部方法不允许向宿主对象添加新属性。

如果 ECMAScript 代码监测到宿主对象的 [[Extensible]] 内部属性是 false,那么它以后必须不能再改为 true

表 9 - 只在某些对象中定义的内部属性
内部属性 值的类型范围 说明
[[PrimitiveValue]] 原始类型 与此对象的内部状态信息关联。对于标准内置对象只能用 BooleanDateNumberString 对象实现 [[PrimitiveValue]]
[[Construct]] SpecOp (任意类型的列表) → Object 通过 new 运算符调,创建对象。SpecOp 的参数是通过 new 运算符传的参数。实现了这个内部方法的对象叫做构造器
[[Call]] SpecOp (任意, 任意类型的列表) → 任意引用 运行与此对象关联的代码。通过函数调用表达式调用。SpecOp 的参数是一个 this 对象和函数调用表达式传来的参数组成的列表。实现了这个内部方法的对象是可调用的。只有作为宿主对象的可调用对象才可能返回引用值。
[[HasInstance]] SpecOp (任意) → Boolean 返回一个表示参数对象是否可能是由本对象构建的布尔值。在标准内置 ECMAScript 对象中只有 Function 对象实现 [[HasInstance]]
[[Scope]] 词法环境 一个定义了函数对象执行的环境的词法环境。在标准内置 ECMAScript 对象中只有 Function 对象实现 [[Scope]]
[[FormalParameters]] Strings列表 一个包含 FunctionFormalParameterList 的标识符字符串的可能是空的列表。在标准内置 ECMAScript 对象中只有 Function 对象实现 [[FormalParameterList]] Question.png
[[Code]] ECMAScript 代码 函数的 ECMAScript 代码。在标准内置 ECMAScript 对象中只有 Function 对象实现 [[Code]]
[[TargetFunction]] Object 使用标准内置的 Function.prototype.bind 方法创建的函数对象的目标函数。只有使用 Function.prototype.bind 创建的 ECMAScript 对象才有 [[TargetFunction]] 内部属性。
[[BoundThis]] 任意 使用标准内置的 Function.prototype.bind 方法创建的函数对象的预绑定的 this 值。只有使用 Function.prototype.bind 创建的 ECMAScript 对象才有 [[BoundThis]] 内部属性。
[[BoundArguments]] Note.png 任意类型的列表 使用标准内置的 Function.prototype.bind 方法创建的函数对象的预绑定的参数值。只有使用 Function.prototype.bind 创建的 ECMAScript 对象才有 [[BoundArguments]] 内部属性。
[[Match]] SpecOp (String, index) → MatchResult 测试正则匹配并返回一个 MatchResult 值。在标准内置 ECMAScript 对象中只有 RegExp 对象实现 [[Match]]
[[ParameterMap]] Object 提供参数对象的属性和函数关联的形式参数之间的映射。只有参数对象才有 [[ParameterMap]] 内部属性。

引用规范类型

引用类型用来说明 deletetypeof、赋值运算符这些运算符的行为。例如,在赋值运算中左边的操作数期望产生一个引用。通过赋值运算符左侧运算子的语法案例分析可以但不能完全解释赋值行为,还有个难点:函数调用允许返回引用。承认这种可能性纯粹是为了宿主对象。本规范没有定义返回引用的内置 ECMAScript 函数,并且也不提供返回引用的用户定义函数。(另一个不使用语法案列分析的原因是,那样将会影响规范的很多地方,冗长并且别扭。)

一个引用是个已解析的命名绑定。它由三部分组成:值、引用名称和一个严格引用标志(布尔值)。 值是 undefinedObjectBooleanStringNumber环境记录项中的任意一个。值是 undefined 表示此引用不可以解析为一个绑定。引用名称是一个字符串。

本规范中使用以下抽象操作接近引用的部分:

  • GetBase(V):返回引用V值部分。
  • GetReferencedName(V):返回引用V引用名称部分。
  • IsStrictReference(V):返回引用V严格引用部分。
  • HasPrimitiveBase(V):如果值是 BooleanStringNumber,那么返回 true
  • IsPropertyReference(V):如果值是个 ObjectHasPrimitiveBase(V) 是 true,那么返回 true;否则返回 false
  • IsUnresolvableReference(V):如果值是 undefined 那么返回 true,否则返回 false

本规范使用以下抽象操作来操作引用:

GetValue(V)

  1. 如果 Type(V) 不是引用,返回 V
  2. base 为调用 GetBase(V) 的返回值。
  3. 如果 IsUnresolvableReference(V),抛出一个 ReferenceError 异常。
  4. 如果 IsPropertyReference(V),那么
    1. 如果 HasPrimitiveBase(V) 是 false,那么令 getbase[[Get]] 内部方法 , 否则令 get 为下面定义的特殊的 [[Get]] 内部方法。
    2. base 作为 this 值,传递 GetReferencedName(V) 为参数,调用 get 内部方法,返回结果。
  5. 否则,base 必须是一个环境记录项
  6. 传递 GetReferencedName(V) 和 IsStrictReference(V) 为参数调用 baseGetBindingValue 具体方法,返回结果。

GetValue 中的 V 是原始基值的属性引用时使用下面的 [[Get]] 内部方法。它用 base 作为他的 this 值,其中属性 P 是它的参数。采用以下步骤:

  1. OToObject(base)。
  2. desc 为用属性名 P 调用 O[[GetProperty]] 内部方法的返回值。
  3. 如果 descundefined,返回 undefined
  4. 如果 IsDataDescriptor(desc) 是 true,返回 desc[[Value]]
  5. 否则 IsAccessorDescriptor(desc) 必须是 true,令 getterdesc[[Get]]
  6. 如果 getterundefined,返回 undefined
  7. 提供 base 作为 this 值,无参数形式调用 getter[[Call]] 内部方法,返回结果。
注: 上述方法之外无法访问在第一步创建的对象。实现可以选择不真的创建这个对象。使用这个内部方法给实际属性访问产生可见影响的情况只有在调用访问器函数时。

PutValue(V, W)

  1. 如果 Type(V) 不是引用,抛出一个 ReferenceError 异常。
  2. base 为调用 GetBase(V) 的结果。
  3. 如果 IsUnresolvableReference(V),那么
    1. 如果 IsStrictReference(V) 是 true,那么
      1. 抛出 ReferenceError 异常。
    2. GetReferencedName(V)、Wfalse 作为参数调用全局对象[[Put]] 内部方法。
  4. 否则如果 IsPropertyReference(V),那么
    1. 如果 HasPrimitiveBase(V) 是 false,那么令 putbase[[Put]] 内部方法,否则令 put 为下面定义的特殊的 [[Put]] 内部方法。
    2. base 作为 this 值,用 GetReferencedName(V)、WIsStrictReference(V) 作为参数调用 put 内部方法。
  5. 否则 base 必定是环境数据作为 base 的引用。所以,
    1. GetReferencedName(V)、WIsStrictReference(V) 作为参数调用 baseSetMutableBinding 具体方法。
  6. 返回。

PutValue 中的 V 是原始基值的属性引用时使用下面的 [[Put]] 内部方法。用 base 作为 this 值,用属性 P,值 W,布尔标志 Throw 作为参数调用它。采用以下步骤:

  1. OToObject(base)。
  2. 如果用 P 作为参数调用 O[[CanPut]] 内部方法的结果是 false,那么
    1. 如果 Throwtrue,那么抛出一个 TypeError 异常。
    2. 否则返回。
  3. ownDesc 为用 P 作为参数调用 O[[GetOwnProperty]] 内部方法的结果。
  4. 如果 IsDataDescriptor(ownDesc) 是 true,那么
    1. 如果 Throwtrue,那么抛出一个 TypeError 异常。
    2. 否则返回。
  5. desc 为用 P 作为参数调用 O[[GetProperty]] 内部方法的结果。这可能是一个自身或继承的访问器属性描述或是一个继承的数据属性描述
  6. 如果 IsAccessorDescriptor(desc) 是 true,那么
    1. setterdesc[[Set]],他不会是 undefined
    2. base 作为 this 值,用只由 W 组成的列表作为参数调用 setter[[Call]] 内部方法。
  7. 否则,这是要在临时对象 O 上创建自身属性的请求。
    1. 如果 Throwtrue,抛出一个 TypeErroe 异常。
  8. 返回。
注: 上述方法之外无法访问在第一步创建的对象。实现可以选择不真的创建这个临时对象。使用这个内部方法给实际属性访问产生可见影响的情况只有在调用访问器函数时,或 Throw 未通过提前错误检查。当 Throwtrue,试图在这个临时对象上创建新属性的任何属性分配操作会抛出一个错误。

列表规范类型

列表类型用于说明 new 表达式、函数调用,和其他需要值的简单列表的算法中的参数列表的计算。列表类型的值是简单排序的一些值的序列,此序列可以是任意长度。

完结规范类型

完结类型用于说明执行将控制转移到外部的声明(breakcontinuereturnthrow)的行为。完结类型的值是由三部分组成,形如 (type,value,target),其中 typenormalbreakcontinuereturnthrow 之一,value 是任何 ECMASCript语言值emptytarget 是任何 ECMAScript 的 Identifierempty。如果 cv 是一个完结类型的值,那么 cv.typecv.valuecv.target 可以被用来明确地表示其成员值。

术语 “非常规完结” 是指任何 type 不是 normal 的完结。

属性描述符及属性标识符规范类型

属性描述符类型是用来解释命名属性具体操作的特性集。属性描述符类型的值是记录项,由命名字段组成,每个字段的名称是一个特性名并且它的值是一个相应的特性值,这些特性指定在 8.6.1。此外,任何字段都可能存在或不存在。

根据是否存在或使用了某些字段,属性描述符的值可进一步划分为数据属性描述符访问器属性描述符。一个数据属性描述符里包括叫做 [[Value]][[Writable]] 的字段。一个访问器属性描述符里包括叫做 [[Get]][[Set]] 的字段。任何属性描述都可能有名为 [[Enumerable]][[Configurable]] 的字段。一个属性描述符不能同时是数据属性描述符和访问器属性描述符;但是,它可能二者都不是。一个通用属性描述符是,既不是数据属性描述符也不是访问器属性描述符的属性描述符值。一个完全填充属性描述符是访问器属性描述符或数据属性描述符,并且拥有 表5表6 里定义的所有属性特性对应的字段。

本规范中为了便于标记,使用一种类似对象字面量的语法来定义属性描述符。例如,属性描述符 {[[Value]]: 42, [[Writable]]: false, [[Configurable]]: true},就定义了一个数据属性描述符。字段名称的顺序并不重要。任何没有明确列出的字段被认为是不存在的。

在规范中的文本和算法里,可用点符号来指明一个属性描述符的特定字段。例如,如果 D 是一个属性描述符,那么 D.[[Value]] 是“D 的 [[Value]] 字段”的简写。

属性标识符类型用于关联属性名称与属性描述符。属性标识符类型的值是 (name, descriptor) 形式的一对值,其中 name 是一个字符串和 descriptor 是一个属性描述符值。

在本规范中使用以下的抽象操作来操作属性描述符值:

IsAccessorDescriptor(Desc)

当用属性描述符 Desc 调用抽象操作 IsAccessorDescriptor,采用以下步骤:

  1. 如果 Descundefined,那么返回 false
  2. 如果Desc.[[Get]]Desc.[[Set]] 都不存在,则返回 false
  3. 返回 true

IsDataDescriptor(Desc)

当用属性描述符 Desc 调用抽象操作 IsDataDescriptor,采用以下步骤:

  1. 如果 Descundefined,那么返回 false
  2. 如果 Desc.[[Value]]Desc.[[Writable]] 都不存在,则返回 false
  3. 返回 true

IsGenericDescriptor(Desc)

当用属性描述符 Desc 调用抽象操作 IsGenericDescriptor,采用以下步骤:

  1. 如果 Descundefined,那么返回 false
  2. 如果 IsAccessorDescriptor(Desc) 和 IsDataDescriptor(Desc) 都是 false,则返回 true
  3. 返回 false

FromPropertyDescriptor(Desc)

当用属性描述符 Desc 调用抽象操作 FromPropertyDescriptor,采用以下步骤:

假定以下算法的 Desc[[GetOwnProperty]] 返回的完全填充属性描述符

  1. 如果 Descundefined,那么返回 undefined
  2. obj 为仿佛使用 new Object() 表达式创建的新对象,这里的 Object标准内置构造器名。
  3. 如果 IsDataDescriptor(Desc) 是 true,则
    1. 用参数 "value"属性描述符 {[[Value]]: Desc.[[Value]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
    2. 用参数 "writable"属性描述符 {[[Value]]: Desc.[[Writable]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
  4. 否则,IsAccessorDescriptor(Desc) 必定是 true,所以
    1. 用参数 "get"属性描述符 {[[Value]]: Desc.[[Get]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
    2. 用参数 "set"属性描述符 {[[Value]]: Desc.[[Set]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
  5. 用参数 "enumerable"属性描述符 {[[Value]]: Desc.[[Enumerable]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
  6. 用参数 "configurable"属性描述符 {[[Value]]: Desc.[[Configurable]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}、false 调用 obj[[DefineOwnProperty]] 内部方法。
  7. 返回 obj

ToPropertyDescriptor(Obj)

当用对象 Obj 调用抽象操作 ToPropertyDescriptor,采用以下步骤:

  1. 如果 Type(Obj) 不是 Object,抛出一个 TypeError 异常。
  2. desc 为创建初始不包含字段的新属性描述的结果。
  3. 如果用参数 "enumerable" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. enum 为用参数 "enumerable" 调用 Obj[[Get]] 内部方法的结果。
    2. 设定 desc[[Enumerable]] 字段为 ToBoolean(enum)。
  4. 如果参数 "configurable" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. conf 为用参数 "configurable" 调用 Obj[[Get]] 内部方法的结果。
    2. 设定 desc[[Configurable]] 字段为 ToBoolean(conf)。
  5. 如果参数 "value" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. value 为用参数 "value" 调用 Obj[[Get]] 内部方法的结果。
    2. 设定 desc[[Value]] 字段为 value
  6. 如果参数 "writable" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. writable 为用参数 "writable" 调用 Obj[[Get]] 内部方法的结果。
    2. 设定 desc[[Writable]] 字段为 ToBoolean(writable)。
  7. 如果参数 "get" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. getter 为用参数 "get" 调用 Obj[[Get]] 内部方法的结果。
    2. 如果 IsCallable(getter) 是 false 并且 getter 不是 undefined,则抛出一个 TypeError 异常。
    3. 设定 desc[[Get]] 字段为 getter
  8. 如果参数 "set" 调用 Obj[[HasProperty]] 内部方法的结果是 true,则
    1. setter 为用参数 "set" 调用 Obj[[Get]] 内部方法的结果。
    2. 如果 IsCallable(setter) 是 false 并且 setter 不是 undefined,则抛出一个 TypeError 异常。
    3. 设定 desc[[Set]] 字段为 setter
  9. 如果存在 desc.[[Get]]desc.[[Set]],则
    1. 如果存在 desc.[[Value]]desc.[[Writable]],则抛出一个 TypeError 异常。
  10. 返回 desc

词法环境和环境记录项规范类型

词法环境环境记录项类型用于说明在嵌套的函数或块中的名称解析行为。这些类型和他们的操作定义在第 10 章

对象内部方法的算法

在以下算法说明中假定 O 是一个原生 ECMAScript 对象,P 是一个字符串,Desc 是一个属性说明记录,Throw 是一个布尔标志。

[[GetOwnProperty]] (P)

当用属性名 P 调用 O[[GetOwnProperty]] 内部方法,采用以下步骤:

  1. 如果 O 不包含名为 P 的自身属性,返回 undefined
  2. D 为无字段的新建属性描述
  3. XO 的名为 P 的自身属性。
  4. 如果 X数据属性,则
    1. 设定 D.[[Value]]X[[Value]] 特性。
    2. 设定 D.[[Writable]]X[[Writable]] 特性。
  5. 否则 X访问器属性,所以
    1. 设定 D.[[Get]]X[[Get]] 特性。
    2. 设定 D.[[Set]]X[[Set]] 特性。
  6. 设定 D.[[Enumerable]]X[[Enumerable]] 特性。
  7. 设定 D.[[Configurable]]X[[Configurable]] 特性。
  8. 返回 D

然而,如果 O 是一个 String 对象,它就具有一个更细致的 [[GetOwnProperty]] 内部方法。

[[GetProperty]] (P)

当用属性名 P 调用 O[[GetProperty]] 内部方法,采用以下步骤:

  1. prop 为用属性名 P 调用 O[[GetOwnProperty]] 内部方法的结果。
  2. 如果 prop 不是 undefined,返回 prop
  3. protoO[[Prototype]] 内部属性值。
  4. 如果 protonull,返回 undefined
  5. 用参数 P 调用 proto[[GetProperty]] 内部方法,返回结果。

[[Get]] (P)

当用属性名 P 调用 O[[Get]] 内部方法,采用以下步骤:

  1. desc 为用属性名 P 调用 O[[GetProperty]] 内部方法的结果。
  2. 如果 descundefined,返回 undefined
  3. 如果 IsDataDescriptor(desc) 是 true,返回 desc.[[Value]]
  4. 否则,IsAccessorDescriptor(desc) 必定是真,所以,令 getterdesc.[[Get]]
  5. 如果 getterundefined,返回 undefined
  6. O 作为 this,无参数调用 getter[[Call]] 内部方法,返回结果。

[[CanPut]] (P)

当用属性名 P 调用 O[[CanPut]] 内部方法,采用以下步骤:

  1. desc 为用参数 P 调用 O[[GetOwnProperty]] 内部方法的结果。
  2. 如果 desc 不是 undefined,则
    1. 如果 IsAccessorDescriptor(desc) 是 true,则
      1. 如果 desc.[[Set]]undefined,则返回 false
      2. 否则返回 true
    2. 否则,desc 必定是数据属性,所以返回 desc.[[Writable]] 的值。
  3. protoO[[Prototype]] 内部属性。
  4. 如果 protonull,则返回 O[[Extensible]] 内部属性的值。
  5. inherited 为用属性名 P 调用 proto[[GetProperty]] 内部方法的结果。
  6. 如果 inheritedundefined,返回 O[[Extensible]] 内部属性的值。
  7. 如果 IsAccessorDescriptor(inherited) 是 true,则
    1. 如果 inherited.[[Set]]undefined,则返回 false
    2. 否则返回 true
  8. 否则,inherited 必定是数据属性
    1. 如果 O[[Extensible]] 内部属性是 false,返回 false
    2. 否则返回 inherited.[[Writable]] 的值。

宿主对象可以定义受额外约束的 [[Put]] 操作。如果可能,宿主对象不应该在 [[CanPut]] 返回 false 的情况下允许 [[Put]] 操作。

[[Put]] (P, V, Throw)

当用属性名 P,值 V,布尔值 Throw 调用 O[[Put]] 内部方法,采用以下步骤:

  1. 如果用参数 P 调用 O[[CanPut]] 内部方法的结果是 false,则
    1. 如果 Throwtrue,则抛出一个 TypeError 异常。
    2. 否则返回。
  2. ownDesc 为用参数 P 调用 O[[GetOwnProperty]] 内部方法的结果。
  3. 如果 IsDataDescriptor(ownDesc) 是 true,则
    1. valueDesc属性描述 {[[Value]]: V}。
    2. 用参数 PvalueDescThrow 调用 O[[DefineOwnProperty]] 内部方法。
    3. 返回。
  4. desc 为用参数 P 调用 O[[GetProperty]] 内部方法的结果。这可能是自身或继承的访问器属性描述或者是继承的数据属性描述。
  5. 如果 IsAccessorDescriptor(desc) 是 true,则
    1. setterdesc.[[Set]](不会是 undefined)。
    2. O 作为 thisV 作为唯一参数调用 setter[[Call]] 内部方法。
  6. 否则,按照以下步骤在对象 O 上创建名为 P 的命名数据属性。
    1. newDesc属性描述 {[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}。
    2. 用参数 PnewDescThrow 调用 O[[DefineOwnProperty]] 内部方法。
  7. 返回。

[[HasProperty]] (P)

当用属性名 P 调用 O[[HasProperty]] 内部方法,采用以下步骤:

  1. desc 为用属性名 P 调用 O[[GetProperty]] 内部方法的结果。
  2. 如果 descundefined,则返回 false
  3. 否则返回 true

[[Delete]] (P, Throw)

当用属性名 P 和布尔值 Throw 调用 O[[Delete]] 内部方法,采用以下步骤:

  1. desc 为用属性名 P 调用 O[[GetOwnProperty]] 内部方法的结果。
  2. 如果 descundefined,则返回 true
  3. 如果 desc.[[Configurable]]true,则
    1. O 上删除名为 P 的自身属性。
    2. 返回 true
  4. 否则如果 Throw,则抛出一个 TypeError 异常。
  5. 返回 false

[[DefaultValue]] (暗示)

暗示 String 时调用 O[[DefaultValue]] 内部方法,采用以下步骤:

  1. toString 为用参数 "toString" 调用对象 O[[Get]] 内部方法的结果。
  2. 如果 IsCallable(toString) 是 true,则
    1. str 为用 O 作为 this 值,空参数列表调用 toString[[Call]] 内部方法的结果。
    2. 如果 str原始值,返回 str
  3. valueOf 为用参数 "valueOf" 调用对象 O[[Get]] 内部方法的结果。
  4. 如果 IsCallable(valueOf) 是 true,则
    1. val 为用 O 作为 this 值,空参数列表调用 valueOf[[Call]] 内部方法的结果。
    2. 如果 val原始值,返回 val
  5. 抛出一个 TypeError 异常。

暗示 Number 时调用 O[[DefaultValue]] 内部方法,采用以下步骤:

  1. valueOf 为用参数 "valueOf" 调用对象 O[[Get]] 内部方法的结果。
  2. 如果 IsCallable(valueOf) 是 true,则
    1. val 为用 O 作为 this 值,空参数列表调用 valueOf[[Call]] 内部方法的结果。
    2. 如果 val原始值,返回 val
  3. toString 为用参数 "toString" 调用对象 O[[Get]] 内部方法的结果。
  4. 如果 IsCallable(toString) 是 true,则
    1. str 为用 O 作为 this 值,空参数列表调用 toString[[Call]] 内部方法的结果。
    2. 如果 str原始值,返回 str
  5. 抛出一个 TypeError 异常。

当没有指定 暗示 时调用 O[[DefaultValue]] 内部方法,除了 Date 对象的行为用 暗示 String 来处理以外,其它情况的行为都作为 暗示 Number 来处理。

上面说明的 [[DefaultValue]] 在原生对象中只能返回原始值。如果一个宿主对象实现了它自身的 [[DefaultValue]] 内部方法,那么必须确保其 [[DefaultValue]] 内部方法只能返回原始值

[[DefineOwnProperty]] (P, Desc, Throw)

在以下算法中,术语 “拒绝” 是指 “如果 Throwtrue,则抛出 TypeError 异常,否则返回 false”。算法包含测试具体值的属性描述 Desc 的各种字段的步骤。这种方式测试的字段事实上不需要真的在 Desc 里。如果一个字段不存在则将其值看作是 false

当用属性名 P属性描述 Desc,布尔值 Throw 调用 O[[DefineOwnProperty]] 内部方法,采用以下步骤:

  1. current 为用属性名 P 调用 O[[GetOwnProperty]] 内部属性的结果。
  2. extensibleO[[Extensible]] 内部属性值。
  3. 如果 currentundefined 并且 extensiblefalse,则拒绝
  4. 如果 currentundefined 并且 extensibletrue,则
    1. 如果 IsGenericDescriptor(Desc) 或 IsDataDescriptor(Desc) 是 true,则
      1. O 上创建名为 P 的自身数据属性,Desc 描述了它的 [[Value]][[Writable]][[Enumerable]][[Configurable]] 特性值。如果 Desc 的某特性字段值不存在,那么设定新建属性的此特性为默认值。
    2. 否则,Desc 必定是访问器属性描述,所以
      1. O 上创建名为 P 的自身访问器属性,Desc 描述了它的 [[Get]][[Set]][[Enumerable]][[Configurable]] 特性值。如果 Desc 的某特性字段值不存在,那么设定新建属性的此特性为默认值。
  5. 如果 Desc 不存在任何字段,返回 true
  6. 如果 Desc 的任何字段都出现在 current 中,并且用 SameValue 算法比较 Desc 中每个字段值和 current 里对应字段值,结果相同,则返回 true
  7. 如果 current[[Configurable]] 字段是 false,则
    1. 如果 Desc[[Configurable]] 字段是 true,则拒绝
    2. 如果 Desc[[Enumerable]] 字段,并且 current 和 Desc 的 [[Enumerable]] 字段相互布尔否定,则拒绝
  8. IsGenericDescriptor(Desc) 是 true,则不需要进一步验证。
  9. 否则,如果 IsDataDescriptor(current) 和 IsDataDescriptor(Desc) 的结果不同,则
    1. 如果 current[[Configurable]] 字段是 false,则拒绝
    2. 如果 IsDataDescriptor(current) 是 true,则
      1. 将对象 O 的名为 P 的数据属性转换为访问器属性。保留转换属性的 [[Configurable]][[Enumerable]] 特性的现有值,并且设定属性的其余特性为其默认值。
    3. 否则
      1. 将对象 O 的名为 P 的访问器属性转换为数据属性。保留转换属性的 [[Configurable]][[Enumerable]] 特性的现有值,并且设定属性的其余特性为其默认值。
  10. 否则,如果 IsDataDescriptor(current) 和 IsDataDescriptor(Desc) 都是 true,则
    1. 如果 current[[Configurable]] 字段是 false,则
      1. 如果 current[[Writable]] 字段是 false 并且 Desc[[Writable]] 字段是 true,则拒绝
      2. 如果 current[[Writable]] 字段是 false,则
        1. 如果 Desc[[Value]] 字段,并且 SameValue(Desc. [[Value]], current. [[Value]]) 是 false,则拒绝
    2. 否则,current[[Configurable]] 字段是 true,所以可接受任何更改。
  11. 否则 IsAccessorDescriptor(current) 和 IsAccessorDescriptor(Desc) 都是 true,所以
    1. 如果 current[[Configurable]] 字段是 false,则
      1. 如果 Desc[[Set]] 字段,并且 SameValue(Desc. [[Set]], current. [[Set]]) 是 false,则拒绝
      2. 如果 Desc[[Get]] 字段,并且 SameValue(Desc. [[Get]], current. [[Get]]) 是 false,则拒绝
  12. Desc 拥有所有特性字段,设定对象 O 的名为 P 的属性的对性特性为这些字段值。
  13. 返回 true

然而,如果 O 是一个 Array 对象,它就具有一个更细致的 [[DefineOwnProperty]] 内部方法。

注: 如果 current[[Configurable]] 字段是 true,那么步骤 10.b 允许 Desc 的任何字段与 current 对应的字段不同。这甚至可以改变 [[Writable]] 特性为 false 的属性的 [[Value]]。允许这种情况是因为值是 true[[Configurable]] 特性会允许按照:首先设定 [[Writable]]true,然后设定新 [[Value]][[Writable]] 设为 false 的顺序调用。