JavaScript 数据类型和数据结构

编程语言都有内置的数据结构,但这些数据结构通常因一种语言而异。本文尝试列出 JavaScript 中可用的内置数据结构以及它们具有哪些属性。这些可用于构建其他数据结构。

¥Programming languages all have built-in data structures, but these often differ from one language to another. This article attempts to list the built-in data structures available in JavaScript and what properties they have. These can be used to build other data structures.

语言概述 提供了常见数据类型的类似摘要,但与其他语言进行了更多比较。

¥The language overview offers a similar summary of the common data types, but with more comparisons to other languages.

动态类型和弱类型

¥Dynamic and weak typing

JavaScript 是一种带有 动态类型动态 语言。JavaScript 中的变量不直接与任何特定值类型相关联,并且可以为任何变量分配(和重新分配)所有类型的值:

¥JavaScript is a dynamic language with dynamic types. Variables in JavaScript are not directly associated with any particular value type, and any variable can be assigned (and re-assigned) values of all types:

js
let foo = 42; // foo is now a number
foo = "bar"; // foo is now a string
foo = true; // foo is now a boolean

JavaScript 也是一种 弱类型 语言,这意味着当操作涉及不匹配的类型时,它允许隐式类型转换,而不是抛出类型错误。

¥JavaScript is also a weakly typed language, which means it allows implicit type conversion when an operation involves mismatched types, instead of throwing type errors.

js
const foo = 42; // foo is a number
const result = foo + "1"; // JavaScript coerces foo to a string, so it can be concatenated with the other operand
console.log(result); // 421

隐式强制转换非常方便,但当转换发生在非预期位置或预期发生在另一个方向(例如,字符串到数字而不是数字到字符串)时,可能会产生微妙的错误。对于 symbolsBigInts,JavaScript 有意禁止某些隐式类型转换。

¥Implicit coercions are very convenient, but can create subtle bugs when conversions happen where they are not expected, or where they are expected to happen in the other direction (for example, string to number instead of number to string). For symbols and BigInts, JavaScript has intentionally disallowed certain implicit type conversions.

原始值

¥Primitive values

对象 之外的所有类型都定义直接在语言最底层别表示的 immutable 值。我们将这些类型的值称为原始值。

¥All types except Object define immutable values represented directly at the lowest level of the language. We refer to values of these types as primitive values.

null 之外的所有原始类型都可以通过 typeof 运算符进行测试。typeof null 返回 "object",因此必须使用 === null 来测试 null

¥All primitive types, except null, can be tested by the typeof operator. typeof null returns "object", so one has to use === null to test for null.

nullundefined 之外的所有基元类型都有其相应的对象封装类型,这些类型提供了处理基元值的有用方法。例如,Number 对象提供了像 toExponential() 这样的方法。当访问原始值的属性时,JavaScript 会自动将该值封装到相应的封装对象中,并访问该对象上的属性。但是,访问 nullundefined 上的属性会引发 TypeError 异常,这需要引入 可选链接 运算符。

¥All primitive types, except null and undefined, have their corresponding object wrapper types, which provide useful methods for working with the primitive values. For example, the Number object provides methods like toExponential(). When a property is accessed on a primitive value, JavaScript automatically wraps the value into the corresponding wrapper object and accesses the property on the object instead. However, accessing a property on null or undefined throws a TypeError exception, which necessitates the introduction of the optional chaining operator.

类型 typeof 返回值 对象封装器
无效的 "object" N/A
不明确的 "undefined" N/A
布尔值 "boolean" Boolean
数字 "number" Number
BigInt "bigint" BigInt
字符串 "string" String
符合 "symbol" Symbol

对象封装类的参考页包含有关每种类型可用的方法和属性的更多信息,以及原始类型本身语义的详细描述。

¥The object wrapper classes' reference pages contain more information about the methods and properties available for each type, as well as detailed descriptions for the semantics of the primitive types themselves.

空类型

¥Null type

Null 类型只包含一个值:null

¥The Null type is inhabited by exactly one value: null.

未定义类型

¥Undefined type

Undefined 类型只包含一个值:undefined

¥The Undefined type is inhabited by exactly one value: undefined.

从概念上讲,undefined 表示缺少值,而 null 表示缺少对象(这也可以为 typeof null === "object" 找借口)。当某些内容没有值时,语言通常默认为 undefined

¥Conceptually, undefined indicates the absence of a value, while null indicates the absence of an object (which could also make up an excuse for typeof null === "object"). The language usually defaults to undefined when something is devoid of a value:

  • 没有值 (return;) 的 return 语句隐式返回 undefined
  • 访问不存在的 object 属性 (obj.iDontExist) 将返回 undefined
  • 没有初始化的变量声明 (let x;) 隐式地将变量初始化为 undefined
  • 许多方法(例如 Array.prototype.find()Map.prototype.get())在未找到元素时返回 undefined

null 在核心语言中使用得很少。最重要的地方是 原型链 的结尾 - 随后,与原型交互的方法,例如 Object.getPrototypeOf()Object.create() 等,接受或返回 null 而不是 undefined

¥null is used much less often in the core language. The most important place is the end of the prototype chain — subsequently, methods that interact with prototypes, such as Object.getPrototypeOf(), Object.create(), etc., accept or return null instead of undefined.

nullkeyword,但 undefined 是普通的 identifier,恰好是全局属性。实际上,差异很小,因为 undefined 不应被重新定义或隐藏。

¥null is a keyword, but undefined is a normal identifier that happens to be a global property. In practice, the difference is minor, since undefined should not be redefined or shadowed.

布尔类型

¥Boolean type

Boolean 类型表示一个逻辑实体并包含两个值:truefalse

¥The Boolean type represents a logical entity and is inhabited by two values: true and false.

布尔值通常用于条件运算,包括 三元运算符if...elsewhile 等。

¥Boolean values are usually used for conditional operations, including ternary operators, if...else, while, etc.

号码类型

¥Number type

Number 型是 双精度 64 位二进制格式 IEEE 754 值 型。它能够存储 2-1074 (Number.MIN_VALUE) 和 21024 (Number.MAX_VALUE) 之间的正浮点数以及 -2 之间的负浮点数 >-1074 和 -21024,但它只能安全地存储 -(253 − 1) (Number.MIN_SAFE_INTEGER) 到 2<sup 范围内的整数 >53 - 1 (Number.MAX_SAFE_INTEGER)。超出此范围,JavaScript 无法再安全地表示整数;相反,它们将由双精度浮点近似值表示。你可以使用 Number.isSafeInteger() 检查数字是否在安全整数范围内。

¥The Number type is a double-precision 64-bit binary format IEEE 754 value. It is capable of storing positive floating-point numbers between 2-1074 (Number.MIN_VALUE) and 21024 (Number.MAX_VALUE) as well as negative floating-point numbers between -2-1074 and -21024, but it can only safely store integers in the range -(253 − 1) (Number.MIN_SAFE_INTEGER) to 253 − 1 (Number.MAX_SAFE_INTEGER). Outside this range, JavaScript can no longer safely represent integers; they will instead be represented by a double-precision floating point approximation. You can check if a number is within the range of safe integers using Number.isSafeInteger().

±(2-1074 到 21024) 范围之外的值会自动转换:

¥Values outside the range ±(2-1074 to 21024) are automatically converted:

+Infinity-Infinity 的行为与数学无穷大类似,但有一些细微的差别;详情请参见 Number.POSITIVE_INFINITYNumber.NEGATIVE_INFINITY

¥+Infinity and -Infinity behave similarly to mathematical infinity, but with some slight differences; see Number.POSITIVE_INFINITY and Number.NEGATIVE_INFINITY for details.

Number 类型只有一个值,具有多种表示形式:0 表示为 -0+0(其中 0+0 的别名)。实际上,不同的表示形式之间几乎没有区别;例如,+0 === -0true。但是,当你除以零时,你会注意到这一点:

¥The Number type has only one value with multiple representations: 0 is represented as both -0 and +0 (where 0 is an alias for +0). In practice, there is almost no difference between the different representations; for example, +0 === -0 is true. However, you are able to notice this when you divide by zero:

js
console.log(42 / +0); // Infinity
console.log(42 / -0); // -Infinity

NaN ("不是一个数字") 是一种特殊类型的数值,通常在算术运算的结果无法表示为数字时遇到。它也是 JavaScript 中唯一不等于自身的值。

¥NaN ("Not a Number") is a special kind of number value that's typically encountered when the result of an arithmetic operation cannot be expressed as a number. It is also the only value in JavaScript that is not equal to itself.

尽管数字在概念上是 "数学值" 并且始终隐式浮点编码,但 JavaScript 提供了 按位运算符。应用按位运算符时,数字首先转换为 32 位整数。

¥Although a number is conceptually a "mathematical value" and is always implicitly floating-point-encoded, JavaScript provides bitwise operators. When applying bitwise operators, the number is first converted to a 32-bit integer.

注意:尽管按位运算符可用于使用 位掩码 来表示单个数字内的多个布尔值,但这通常被认为是一种不好的做法。JavaScript 提供了其他方法来表示一组布尔值(例如布尔值数组,或者将布尔值分配给命名属性的对象)。位掩码还往往会使代码更难以阅读、理解和维护。

¥Note: Although bitwise operators can be used to represent several Boolean values within a single number using bit masking, this is usually considered a bad practice. JavaScript offers other means to represent a set of Booleans (like an array of Booleans, or an object with Boolean values assigned to named properties). Bit masking also tends to make the code more difficult to read, understand, and maintain.

在非常受限的环境中可能有必要使用此类技术,例如在尝试应对本地存储的限制时,或在极端情况下(例如当网络上的每个位都很重要时)。仅当它是优化尺寸的最后措施时才应考虑该技术。

¥It may be necessary to use such techniques in very constrained environments, like when trying to cope with the limitations of local storage, or in extreme cases (such as when each bit over the network counts). This technique should only be considered when it is the last measure that can be taken to optimize size.

BigInt 类型

¥BigInt type

BigInt 类型是 JavaScript 中的数字基元,可以表示任意大小的整数。使用 BigInts,你可以安全地存储和操作大整数,甚至超出数字的安全整数限制 (Number.MAX_SAFE_INTEGER)。

¥The BigInt type is a numeric primitive in JavaScript that can represent integers with arbitrary magnitude. With BigInts, you can safely store and operate on large integers even beyond the safe integer limit (Number.MAX_SAFE_INTEGER) for Numbers.

BigInt 是通过将 n 附加到整数末尾或通过调用 BigInt() 函数来创建的。

¥A BigInt is created by appending n to the end of an integer or by calling the BigInt() function.

此示例演示了递增 Number.MAX_SAFE_INTEGER 会返回预期结果的位置:

¥This example demonstrates where incrementing the Number.MAX_SAFE_INTEGER returns the expected result:

js
// BigInt
const x = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n
x + 1n === x + 2n; // false because 9007199254740992n and 9007199254740993n are unequal

// Number
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // true because both are 9007199254740992

你可以使用大多数运算符来处理 BigInt,包括 +*-**% — 唯一禁止的运算符是 >>>。BigInt 不是具有相同数学值的 Number 的 严格平等,但它是 loosely

¥You can use most operators to work with BigInts, including +, *, -, **, and % — the only forbidden one is >>>. A BigInt is not strictly equal to a Number with the same mathematical value, but it is loosely so.

BigInt 值并不总是比数字更精确,也并不总是比数字更不精确,因为 BigInt 不能表示小数,但可以更准确地表示大整数。任何一种类型都不必然包含另一种类型,并且它们不能相互替代。如果 BigInt 值与算术表达式中的常规数字混合,或者它们彼此为 隐式转换,则会抛出 TypeError

¥BigInt values are neither always more precise nor always less precise than numbers, since BigInts cannot represent fractional numbers, but can represent big integers more accurately. Neither type entails the other, and they are not mutually substitutable. A TypeError is thrown if BigInt values are mixed with regular numbers in arithmetic expressions, or if they are implicitly converted to each other.

字符串类型

¥String type

String 类型表示文本数据,并被编码为表示 UTF-16 代码单元 的 16 位无符号整数值序列。字符串中的每个元素在字符串中占据一个位置。第一个元素位于索引 0 处,下一个元素位于索引 1 处,依此类推。字符串的 length 是其中的 UTF-16 代码单元的数量,可能与实际的 Unicode 字符数量不对应;有关详细信息,请参阅 String 参考页。

¥The String type represents textual data and is encoded as a sequence of 16-bit unsigned integer values representing UTF-16 code units. Each element in the string occupies a position in the string. The first element is at index 0, the next at index 1, and so on. The length of a string is the number of UTF-16 code units in it, which may not correspond to the actual number of Unicode characters; see the String reference page for more details.

JavaScript 字符串是不可变的。这意味着一旦创建了字符串,就无法对其进行修改。字符串方法根据当前字符串的内容创建新字符串 - 例如:

¥JavaScript strings are immutable. This means that once a string is created, it is not possible to modify it. String methods create new strings based on the content of the current string — for example:

  • 原来使用 substring() 的子串。
  • 使用串联运算符 (+) 或 concat() 连接两个字符串。

当心 "stringly-typing" 你的代码!

¥Beware of "stringly-typing" your code!

使用字符串来表示复杂数据可能很诱人。这样做会带来短期好处:

¥It can be tempting to use strings to represent complex data. Doing this comes with short-term benefits:

  • 通过连接构建复杂的字符串很容易。
  • 字符串很容易调试(你看到的打印内容始终是字符串中的内容)。
  • 字符串是许多 API 的共同点(输入字段本地存储 值、使用 Response.text() 时的 fetch() 响应等),并且很容易只使用字符串。

通过约定,可以用字符串表示任何数据结构。这并不是一个好主意。例如,使用分隔符,可以模拟列表(而 JavaScript 数组更合适)。不幸的是,当分隔符用于 "list" 元素之一时,列表就会被破坏。可以选择转义字符等。所有这些都需要约定并造成不必要的维护负担。

¥With conventions, it is possible to represent any data structure in a string. This does not make it a good idea. For instance, with a separator, one could emulate a list (while a JavaScript array would be more suitable). Unfortunately, when the separator is used in one of the "list" elements, then, the list is broken. An escape character can be chosen, etc. All of this requires conventions and creates an unnecessary maintenance burden.

使用字符串作为文本数据。表示复杂数据时,解析字符串并使用适当的抽象。

¥Use strings for textual data. When representing complex data, parse strings, and use the appropriate abstraction.

符号类型

¥Symbol type

Symbol 是唯一且不可变的原始值,可以用作对象属性的键(见下文)。在某些编程语言中,符号被称为 "atoms"。符号的目的是创建唯一的属性键,保证不会与其他代码中的键冲突。

¥A Symbol is a unique and immutable primitive value and may be used as the key of an Object property (see below). In some programming languages, Symbols are called "atoms". The purpose of symbols is to create unique property keys that are guaranteed not to clash with keys from other code.

对象

¥Objects

在计算机科学中,对象是内存中的一个值,可能被 identifier 引用。在 JavaScript 中,对象是唯一的 mutable 值。事实上,函数 也是具有可调用附加功能的对象。

¥In computer science, an object is a value in memory which is possibly referenced by an identifier. In JavaScript, objects are the only mutable values. Functions are, in fact, also objects with the additional capability of being callable.

属性

¥Properties

在 JavaScript 中,对象可以被视为属性的集合。对于 对象字面量语法,一组有限的属性被初始化;然后可以添加和删除属性。对象属性相当于键值对。属性键是 stringssymbols。属性值可以是任何类型的值,包括其他对象,这使得能够构建复杂的数据结构。

¥In JavaScript, objects can be seen as a collection of properties. With the object literal syntax, a limited set of properties are initialized; then properties can be added and removed. Object properties are equivalent to key-value pairs. Property keys are either strings or symbols. Property values can be values of any type, including other objects, which enables building complex data structures.

对象属性有两种类型:数据属性访问器属性。每个属性都有相应的属性。每个属性都由 JavaScript 引擎内部访问,但你可以通过 Object.defineProperty() 设置它们,或通过 Object.getOwnPropertyDescriptor() 读取它们。你可以在 Object.defineProperty() 页面上阅读有关各种细微差别的更多信息。

¥There are two types of object properties: The data property and the accessor property. Each property has corresponding attributes. Each attribute is accessed internally by the JavaScript engine, but you can set them through Object.defineProperty(), or read them through Object.getOwnPropertyDescriptor(). You can read more about the various nuances on the Object.defineProperty() page.

数据属性

¥Data property

数据属性将键与值关联起来。它可以用以下属性来描述:

¥Data properties associate a key with a value. It can be described by the following attributes:

value

通过属性的 get 访问权限检索到的值。可以是任何 JavaScript 值。

writable

一个布尔值,指示是否可以通过赋值来更改属性。

enumerable

一个布尔值,指示该属性是否可以通过 for...in 循环枚举。另请参阅 可枚举性和属性所有权 了解可枚举性如何与其他函数和语法交互。

configurable

一个布尔值,指示该属性是否可以删除、是否可以更改为访问器属性以及是否可以更改其属性。

访问器属性

¥Accessor property

将键与两个访问器函数(getset)之一关联以检索或存储值。

¥Associates a key with one of two accessor functions (get and set) to retrieve or store a value.

注意:重要的是要认识到它是访问器属性,而不是访问器方法。我们可以通过使用函数作为值来为 JavaScript 对象提供类似类的访问器,但这并不会使该对象成为一个类。

¥Note: It's important to recognize it's accessor property — not accessor method. We can give a JavaScript object class-like accessors by using a function as a value — but that doesn't make the object a class.

访问器属性具有以下属性:

¥An accessor property has the following attributes:

get

使用空参数列表调用的函数,用于在执行对值的 get 访问时检索属性值。另见 getters。可能是 undefined

set

使用包含指定值的参数调用的函数。每当尝试更改指定属性时执行。另见 setters。可能是 undefined

enumerable

一个布尔值,指示该属性是否可以通过 for...in 循环枚举。另请参阅 可枚举性和属性所有权 了解可枚举性如何与其他函数和语法交互。

configurable

一个布尔值,指示该属性是否可以删除、是否可以更改为数据属性以及是否可以更改其属性。

一个对象的 prototype 指向另一个对象或 null — 从概念上讲,它是该对象的隐藏属性,通常表示为 [[Prototype]]。对象 [[Prototype]] 的属性也可以在对象本身上访问。

¥The prototype of an object points to another object or to null — it's conceptually a hidden property of the object, commonly represented as [[Prototype]]. Properties of the object's [[Prototype]] can also be accessed on the object itself.

对象是临时键值对,因此它们通常用作映射。然而,可能存在人机工程学、安全性和性能问题。使用 Map 来存储任意数据。Map 参考 包含对普通对象和用于存储键值关联的映射之间的优缺点的更详细讨论。

¥Objects are ad-hoc key-value pairs, so they are often used as maps. However, there can be ergonomics, security, and performance issues. Use a Map for storing arbitrary data instead. The Map reference contains a more detailed discussion of the pros & cons between plain objects and maps for storing key-value associations.

日期

¥Dates

表示日期时,最好的选择是使用 JavaScript 中内置的 Date 实用程序。

¥When representing dates, the best choice is to use the built-in Date utility in JavaScript.

索引集合:数组和类型化数组

¥Indexed collections: Arrays and typed Arrays

数组 是常规对象,其整数键控属性和 length 属性之间存在特定关系。

¥Arrays are regular objects for which there is a particular relationship between integer-keyed properties and the length property.

此外,数组继承自 Array.prototype,它提供了一些方便的方法来操作数组。例如,indexOf() 在数组中搜索一个值,push() 将一个元素追加到数组中。这使得数组成为表示有序列表的完美候选者。

¥Additionally, arrays inherit from Array.prototype, which provides a handful of convenient methods to manipulate arrays. For example, indexOf() searches a value in the array and push() appends an element to the array. This makes Arrays a perfect candidate to represent ordered lists.

类型化数组 提供了底层二进制数据缓冲区的类似数组的视图,并提供了许多与数组对应项具有相似语义的方法。"类型化数组" 是一系列数据结构的总称,包括 Int8ArrayFloat32Array 等。请查看 类型数组 页面以获取更多信息。类型化数组通常与 ArrayBufferDataView 结合使用。

¥Typed Arrays present an array-like view of an underlying binary data buffer, and offer many methods that have similar semantics to the array counterparts. "Typed array" is an umbrella term for a range of data structures, including Int8Array, Float32Array, etc. Check the typed array page for more information. Typed arrays are often used in conjunction with ArrayBuffer and DataView.

键控集合:映射、集合、WeakMap、WeakSet

¥Keyed collections: Maps, Sets, WeakMaps, WeakSets

这些数据结构将对象引用作为键。SetWeakSet 表示唯一值的集合,而 MapWeakMap 表示键值关联的集合。

¥These data structures take object references as keys. Set and WeakSet represent a collection of unique values, while Map and WeakMap represent a collection of key-value associations.

你可以自己实现 MapSet。然而,由于对象无法比较(例如,在 < "少于" 的意义上),引擎也不会公开其对象的哈希函数,因此查找性能必然是线性的。它们的原生实现(包括 WeakMap)可以具有近似对数到常数时间的查找性能。

¥You could implement Maps and Sets yourself. However, since objects cannot be compared (in the sense of < "less than", for instance), neither does the engine expose its hash function for objects, look-up performance would necessarily be linear. Native implementations of them (including WeakMaps) can have look-up performance that is approximately logarithmic to constant time.

通常,要将数据绑定到 DOM 节点,可以直接在对象上设置属性,或使用 data-* 属性。这样做的缺点是数据可供在同一上下文中运行的任何脚本使用。MapWeakMap 可以轻松地将数据私密地绑定到对象。

¥Usually, to bind data to a DOM node, one could set properties directly on the object, or use data-* attributes. This has the downside that the data is available to any script running in the same context. Maps and WeakMaps make it easy to privately bind data to an object.

WeakMapWeakSet 只允许垃圾可收集值作为键,这些值要么是对象,要么是 非注册符号,并且即使键保留在集合中也可能被收集。它们专门用于 内存使用优化

¥WeakMap and WeakSet only allow garbage-collectable values as keys, which are either objects or non-registered symbols, and the keys may be collected even when they remain in the collection. They are specifically used for memory usage optimization.

结构化数据:JSON

¥Structured data: JSON

JSON(JavaScript 对象表示法)是一种轻量级数据交换格式,源自 JavaScript,但被许多编程语言使用。JSON 构建了可以在不同环境甚至跨语言之间传输的通用数据结构。详细信息请参见 JSON

¥JSON (JavaScript Object Notation) is a lightweight data-interchange format, derived from JavaScript, but used by many programming languages. JSON builds universal data structures that can be transferred between different environments and even across languages. See JSON for more details.

标准库中的更多对象

¥More objects in the standard library

JavaScript 有一个内置对象的标准库。阅读 reference 以了解有关内置对象的更多信息。

¥JavaScript has a standard library of built-in objects. Read the reference to find out more about the built-in objects.

类型强制

¥Type coercion

如上所述,JavaScript 是一种 弱类型 语言。这意味着你通常可以在需要另一种类型的地方使用一种类型的值,并且语言会将其转换为适合你的类型。为此,JavaScript 定义了一些强制规则。

¥As mentioned above, JavaScript is a weakly typed language. This means that you can often use a value of one type where another type is expected, and the language will convert it to the right type for you. To do so, JavaScript defines a handful of coercion rules.

原始强制

¥Primitive coercion

原始强制 过程用于需要原始值的地方,但对于实际类型应该是什么没有强烈的偏好。通常,stringnumberBigInt 同样可以接受。例如:

¥The primitive coercion process is used where a primitive value is expected, but there's no strong preference for what the actual type should be. This is usually when a string, a number, or a BigInt are equally acceptable. For example:

  • Date() 构造函数,当它收到一个不是 Date 实例的参数时 — 字符串表示日期字符串,而数字表示时间戳。
  • + 运算符 — 如果一个操作数是字符串,则执行字符串连接;否则,执行数字加法。
  • == 运算符 — 如果一个操作数是基元,而另一个操作数是对象,则该对象将转换为没有首选类型的基元值。

如果该值已经是基元,则此操作不会执行任何转换。通过按顺序调用 [@@toPrimitive]()(以 "default" 作为提示)、valueOf()toString() 方法,对象将转换为基元。请注意,原始转换在 toString() 之前调用 valueOf(),这与 数字强制 的行为类似,但与 字符串强制 不同。

¥This operation does not do any conversion if the value is already a primitive. Objects are converted to primitives by calling its [@@toPrimitive]() (with "default" as hint), valueOf(), and toString() methods, in that order. Note that primitive conversion calls valueOf() before toString(), which is similar to the behavior of number coercion but different from string coercion.

[@@toPrimitive]() 方法(如果存在)必须返回一个原语 — 返回一个对象会导致 TypeError。对于 valueOf()toString(),如果一个返回对象,则忽略返回值,而使用另一个的返回值;如果两者都不存在,或者都不返回原语,则抛出 TypeError。例如,在以下代码中:

¥The [@@toPrimitive]() method, if present, must return a primitive — returning an object results in a TypeError. For valueOf() and toString(), if one returns an object, the return value is ignored and the other's return value is used instead; if neither is present, or neither returns a primitive, a TypeError is thrown. For example, in the following code:

js
console.log({} + []); // "[object Object]"

{}[] 都没有 [@@toPrimitive]() 方法。{}[] 都从 Object.prototype.valueOf 继承了 valueOf()Object.prototype.valueOf 返回对象本身。由于返回值是一个对象,因此被忽略。因此,改为调用 toString(){}.toString() 返回 "[object Object]",而 [].toString() 返回 "",因此结果是它们的串联:"[object Object]"

¥Neither {} nor [] have a [@@toPrimitive]() method. Both {} and [] inherit valueOf() from Object.prototype.valueOf, which returns the object itself. Since the return value is an object, it is ignored. Therefore, toString() is called instead. {}.toString() returns "[object Object]", while [].toString() returns "", so the result is their concatenation: "[object Object]".

在转换为任何基本类型时,[@@toPrimitive]() 方法始终优先。原始转换的行为一般与数字转换类似,因为优先调用 valueOf();但是,具有自定义 [@@toPrimitive]() 方法的对象可以选择返回任何原语。DateSymbol 对象是唯一重写 [@@toPrimitive]() 方法的内置对象。Date.prototype[@@toPrimitive]()"default" 提示视为 "string",而 Symbol.prototype[@@toPrimitive]() 忽略该提示并始终返回一个符号。

¥The [@@toPrimitive]() method always takes precedence when doing conversion to any primitive type. Primitive conversion generally behaves like number conversion, because valueOf() is called in priority; however, objects with custom [@@toPrimitive]() methods can choose to return any primitive. Date and Symbol objects are the only built-in objects that override the [@@toPrimitive]() method. Date.prototype[@@toPrimitive]() treats the "default" hint as if it's "string", while Symbol.prototype[@@toPrimitive]() ignores the hint and always returns a symbol.

数字强制

¥Numeric coercion

有两种数字类型:数字BigInt。有时,语言特别需要数字或 BigInt(例如 Array.prototype.slice(),其中索引必须是数字);其他时候,它可以容忍其中任何一个,并根据操作数的类型执行不同的操作。对于不允许从其他类型隐式转换的严格强制过程,请参阅 数字强制BigInt 强制转换

¥There are two numeric types: Number and BigInt. Sometimes the language specifically expects a number or a BigInt (such as Array.prototype.slice(), where the index must be a number); other times, it may tolerate either and perform different operations depending on the operand's type. For strict coercion processes that do not allow implicit conversion from the other type, see number coercion and BigInt coercion.

数字强制转换几乎与 数字强制 相同,只是 BigInt 按原样返回而不是导致 TypeError。所有算术运算符都使用数字强制转换,因为它们对于数字和 BigInt 都是重载的。唯一的例外是 一元加,它总是进行数字强制转换。

¥Numeric coercion is nearly the same as number coercion, except that BigInts are returned as-is instead of causing a TypeError. Numeric coercion is used by all arithmetic operators, since they are overloaded for both numbers and BigInts. The only exception is unary plus, which always does number coercion.

其他胁迫

¥Other coercions

除 Null、Undefined 和 Symbol 之外的所有数据类型都有各自的强制转换过程。有关详细信息,请参阅 字符串强制布尔强制对象强制

¥All data types, except Null, Undefined, and Symbol, have their respective coercion process. See string coercion, boolean coercion, and object coercion for more details.

你可能已经注意到,对象可以通过三种不同的路径转换为基元:

¥As you may have noticed, there are three distinct paths through which objects may be converted to primitives:

在所有情况下,[@@toPrimitive]()(如果存在)必须可调用并返回原语,而 valueOftoString 如果不可调用或返回对象,则将被忽略。在该过程结束时,如果成功,则保证结果是一个原语。然后,根据上下文,生成的原语将受到进一步的强制。

¥In all cases, [@@toPrimitive](), if present, must be callable and return a primitive, while valueOf or toString will be ignored if they are not callable or return an object. At the end of the process, if successful, the result is guaranteed to be a primitive. The resulting primitive is then subject to further coercions depending on the context.

也可以看看

¥See also