Skip to content

Commit

Permalink
[zh-cn]: update translation of Map.overview
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonLamv-t committed Oct 3, 2023
1 parent f9145b9 commit 37c4a24
Showing 1 changed file with 127 additions and 77 deletions.
204 changes: 127 additions & 77 deletions files/zh-cn/web/javascript/reference/global_objects/map/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ slug: Web/JavaScript/Reference/Global_Objects/Map

{{JSRef}}

**`Map`** 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者{{Glossary("Primitive", "基本类型")}})都可以作为一个键或一个值。
**`Map`** 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者{{Glossary("Primitive", "原始值")}})都可以作为一个键或一个值。

{{EmbedInteractiveExample("pages/js/map.html", "taller")}}

Expand Down Expand Up @@ -36,95 +36,107 @@ slug: Web/JavaScript/Reference/Global_Objects/Map
<tbody>
<tr>
<th scope="row">意外的键</th>
<td><code>Map</code> 默认情况不包含任何键。只包含显式插入的键。</td>
<td>
<p>一个 <code>Object</code> 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。</p>
<code>Map</code> 默认不包含任何键。它只包含显式存入的键值对。
</td>
<td>
<p>
<code>Object</code> 有原型,因此它包含默认的键,如果不小心的话,它们可能会与你自己的键相冲突。
</p>
<div class="notecard note">
<p><strong>备注:</strong>虽然可以用 {{jsxref("Object.create", "Object.create(null)")}} 来创建一个没有原型的对象,但是这种用法不太常见。</p>
<p>
<strong>请注意:</strong> 这可以通过使用 {{jsxref("Object.create", "Object.create(null)")}} 来绕过,但很少这样做。
</p>
</div>
</td>
</tr>
<tr>
<th scope="row">安全性</th>
<td>
<code>Map</code> 可以安全地与用户提供的键值一起使用。
</td>
<td>
<p>
在 <code>Object</code> 上设置用户提供的键值对可能会允许攻击者覆盖对象的原型,这可能会导致<a href="https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/the-dangers-of-square-bracket-notation.md">对象注入攻击</a>。就像意外的键问题一样,这也可以通过使用<code>null</code>原型对象来缓解。
</p>
</td>
</tr>
<tr>
<th scope="row">键的类型</th>
<td>一个 <code>Map</code> 的键可以是<strong>任意值</strong>,包括函数、对象或任意基本类型。</td>
<td>一个 <code>Object</code> 的键必须是一个 {{jsxref("String")}} 或是 {{jsxref("Symbol")}}。</td>
<td>
<code>Map</code> 的键可以为任何值(包括函数、对象或任何原始值)。
</td>
<td>
<code>Object</code> 的键必须为 {{jsxref("String")}} 或 {{jsxref("Symbol")}}。
</td>
</tr>
<tr>
<th scope="row">键的顺序</th>
<td>
<p><code>Map</code> 中的键是有序的。因此,当迭代的时候,一个 <code>Map</code> 对象以插入的顺序返回键值。</p>
<p>
<code>Map</code> 中的键以简单、直接的方式排序:<code>Map</code> 对象按照插入的顺序迭代条目、键和值。
</p>
</td>
<td>
<p>虽然 <code>Object</code> 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。</p>
<p>自 ECMAScript 2015 规范以来,对象的属性被定义为是有序的;ECMAScript 2020 则额外定义了继承属性的顺序。参见 <a href="https://tc39.es/ecma262/#sec-ordinaryownpropertykeys"
>OrdinaryOwnPropertyKeys</a
>
<a href="https://tc39.es/ecma262/#sec-enumerate-object-properties"
>EnumerateObjectProperties</a
> 抽象规范说明。但是,请注意没有可以迭代对象所有属性的机制,每一种机制只包含了属性的不同子集。({{jsxref("Statements/for...in","for-in")}}
仅包含了以字符串为键的属性;{{jsxref("Object.keys")}}
仅包含了对象自身的、可枚举的、以字符串为键的属性;{{jsxref("Object.getOwnPropertyNames")}}
包含了所有以字符串为键的属性,即使是不可枚举的;{{jsxref("Object.getOwnPropertySymbols")}}
与前者类似,但其包含的是以 <code>Symbol</code> 为键的属性,等等。)
<p>
尽管现在普通的 <code>Object</code> 的键是有序的,但情况并非总是如此,并且其排序比较复杂的。因此,最好不要依赖属性的顺序。
</p>
<p>
该顺序最初仅在 ECMAScript 2015 中为自有属性定义;ECMAScript 2020 还定义了继承属性的顺序。但请注意,没有单一机制可以迭代对象的<strong>所有</strong>属性;各种机制各自包含不同的属性子集。({{jsxref("Statements/for...in", "for-in")}} 仅包含可枚举的字符串键属性;{{jsxref("Object.keys")}} 仅包含可枚举的自有字符串键属性;{{jsxref("Object.getOwnPropertyNames")}} 包括自有的字符串键属性,即使是不可枚举的;{{jsxref("Object.getOwnPropertySymbols")}} 仅对 <code>Symbol</code> 键属性执行相同的操作,等等。)
</p>
</td>
</tr>
<tr>
<th scope="row">Size</th>
<td><code>Map</code> 的键值对个数可以轻易地通过 {{jsxref("Map.prototype.size", "size")}} 属性获取。</td>
<td><code>Object</code> 的键值对个数只能手动计算。</td>
<th scope="row"><p>大小</p></th>
<td>
<code>Map</code>中的项目数量很容易从其 {{jsxref("Map.prototype.size", "size")}} 属性中获得。
</td>
<td>
确定 <code>Object</code> 中的项目数量通常更麻烦,效率也较低。一种常见的方法是通过获取 {{jsxref("Object.keys()")}} 返回的数组的{{jsxref("Array/length", "长度")}}。
</td>
</tr>
<tr>
<th scope="row">迭代</th>
<td><code>Map</code> 是 <a href="/zh-CN/docs/Web/JavaScript/Guide/iterable">可迭代的</a> 的,所以可以直接被迭代。</td>
<td>
<p><code>Object</code> 没有实现 <a
href="/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol"
>迭代协议</a
>,所以使用 JavaSctipt 的 <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of"
>for...of</a
> 表达式并不能直接迭代对象。</p>
<div class="notecard note">
<p><strong>备注:</strong></p>
<ul>
<li>
对象可以实现迭代协议,或者你可以使用 <a
href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys"
><code>Object.keys</code></a
> 或 <a
href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/entries"
><code>Object.entries</code></a
>。
</li>
<li>
<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in"
>for...in</a
>
表达式允许你迭代一个对象的<em>可枚举</em>属性。
</li>
</ul>
</div>
</td>
<th scope="row">迭代</th>
<td>
<code>Map</code> 是<a href="/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols">可迭代对象</a>,所以它可以直接迭代。
</td>
<td>
<p>
<code>Object</code> 没有实现<a href="/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol">可迭代协议</a>,因此对象默认情况下不能直接通过 JavaScript 的 <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of">for...of</a> 语句进行迭代。
</p>
<div class="notecard note">
<p><strong>请注意:</strong></p>
<ul>
<li>
一个对象可以实现迭代协议,或者你可以使用 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys"><code>Object.keys</code></a> 或 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries"><code>Object.entries</code></a> 来获取一个对象的可迭代对象。
</li>
<li>
<a href="/en-US/docs/Web/JavaScript/Reference/Statements/for...in">for...in</a> 语句允许你迭代对象的可枚举属性。
</li>
</ul>
</div>
</td>
</tr>
<tr>
<th scope="row">性能</th>
<td>
<p>在频繁增删键值对的场景下表现更好。</p>
<p>在涉及频繁添加和删除键值对的场景中表现更好。</p>
</td>
<td>
<p>在频繁添加和删除键值对的场景下未作出优化。</p>
<p>未针对频繁添加和删除键值对进行优化。</p>
</td>
</tr>
<tr>
<th scope="row">序列化和解析</th>
<td>
<p>没有元素的序列化和解析的支持。</p>
<p>(但是你可以使用携带 <em>replacer</em> 参数的 {{jsxref("JSON.stringify()")}} 创建一个自己的对 <code>Map</code> 的序列化和解析支持。参见 Stack Overflow 上的提问:<a href="https://stackoverflow.com/q/29085197/">How do you JSON.stringify an ES6 Map?</a>)</p>
<p>没有对序列化或解析的原生支持。</p>
<p>
(但你可以通过使用 {{jsxref("JSON.stringify()")}} 及其 <em>replacer</em> 参数和 {{jsxref("JSON.parse()")}} 及其 <em>reviver</em> 参数来为 <code>Map</code> 构建自己的序列化和解析支持。参见 Stack Overflow 问题 <a href="https://stackoverflow.com/q/29085197/">How do you JSON.stringify an ES6 Map?</a>)。
</p>
</td>
<td>
<p>原生的由 {{jsxref("Object")}} 到 JSON 的序列化支持,使用 {{jsxref("JSON.stringify()")}}。</p>
<p>原生的由 JSON 到 {{jsxref("Object")}} 的解析支持,使用 {{jsxref("JSON.parse()")}}。</p>
<p>原生支持使用 {{jsxref("JSON.stringify()")}} 序列化 {{jsxref("Object")}} 到 JSON。</p>
<p>原生支持使用 {{jsxref("JSON.parse()")}} 解析 JSON 为 {{jsxref("Object")}}。</p>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -166,45 +178,84 @@ contacts.delete("Jessie"); // true
console.log(contacts.size); // 1
```

### 类似 Map 的浏览器 API

**浏览器中类似 Map 的对象**(或称为“maplike 对象”)是 [Web API](/zh-CN/docs/Web/API) 接口,其行为在很多方面都类型 `Map`

就像 `Map` 一样,对象中的条目可以以添加的顺序迭代。类似 `Map` 的对象和 `Map` 具有相同的属性和方法。但是,与 `Map` 不同的是,它们仅允许每个条目中的键和值具有特定预定义的类型。

允许的类型在规范 IDL 定义中设置。例如,{{domxref("RTCStatsReport")}} 是一个类似 `Map` 的对象,必须使用字符串作为键,对象作为值。这是在规范 IDL 中定义的:

```webidl
interface RTCStatsReport {
readonly maplike<DOMString, object>;
};
```

类似 `Map` 的对象可以是只读的,也可以是可写的(参见上面 IDL 中的 `readonly` 关键字)。

- 只读的类似 `Map` 对象具有 [`size`](#map.prototype.size) 属性,以及这些方法:[`entries()`](#map.prototype.entries)[`forEach()`](#map.prototype.foreach)[`keys()`](#map.prototype.keys)[`values()`](#map.prototype.values)[`@@iterator`](#map.prototypeiterator)
- 可写的类似 `Map` 对象还额外具有这些方法:[`clear()`](#map.prototype.clear)[`delete()`](#map.prototype.delete)[`set()`](#map.prototype.set)

除了对键和值类型的限制外,其方法和属性的行为与 `Map` 中的对应实体相同。

以下是浏览器中只读的类似 `Map` 对象的示例:

- {{domxref("AudioParamMap")}}
- {{domxref("RTCStatsReport")}}
- {{domxref("EventCounts")}}
- {{domxref("KeyboardLayoutMap")}}
- {{domxref("MIDIInputMap")}}
- {{domxref("MIDIOutputMap")}}

## 构造函数

- {{jsxref("Map/Map", "Map()")}}
- : 创建 `Map` 对象。

## 静态属性

- {{jsxref("Map.@@species", "get Map[@@species]")}}
- {{jsxref("Map/@@species", "Map[@@species]")}}
- : 用于创建派生对象的构造函数。

## 静态方法

- {{jsxref("Map.groupBy()")}}
- : 根据提供的回调函数返回的值将给定的可迭代对象分组。最终返回的 `Map` 对象使用测试函数返回的唯一值作为键,可用于获取每个组的元素数组。

## 实例属性

这些属性在 `Map.prototype` 上定义,并由所有 `Map` 实例共享。

- {{jsxref("Object/constructor", "Map.prototype.constructor")}}
- : 创建实例对象的构造函数。对于 `Map` 实例,初始值为 {{jsxref("Map/Map", "Map")}} 构造函数。
- {{jsxref("Map.prototype.size")}}
- : 返回 `Map` 对象中的键值对数量。
- `Map.prototype[@@toStringTag]`
- : [`@@toStringTag`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) 属性的初始值是字符串 `"Map"`。该属性在 {{jsxref("Object.prototype.toString()")}} 中使用。

## 实例方法

- {{jsxref("Map.prototype.clear()")}}
- : 移除 `Map` 对象中所有的键值对。
- {{jsxref("Map.prototype.delete()")}}
- : 移除 `Map` 对象中指定的键值对,如果键值对存在并成功被移除,返回 `true`,否则返回 `false`。调用 `delete` 后再调用 `map.has(key)` 将返回 `false`
- {{jsxref("Map.prototype.entries()")}}
- : 返回一个新的迭代器对象,其包含 `Map` 对象中所有键值对 `[key, value]` 二元数组,以插入顺序排列。
- {{jsxref("Map.prototype.forEach()")}}
- : 以插入顺序为 `Map` 对象中的每个键值对调用一次 `callbackFn`。如果为 `forEach` 提供了 `thisArg` 参数,则它将作为每一次 callback 的 `this` 值。
- {{jsxref("Map.prototype.get()")}}
- : 返回与指定的键 `key` 关联的值,若不存在关联的值,则返回 `undefined`
- {{jsxref("Map.prototype.has()")}}
- : 返回一个布尔值,用来表明 `Map` 对象中是否存在与指定的键 `key` 关联的值。
- {{jsxref("Map.prototype.keys()")}}
- : 返回一个新的迭代器对象,其包含 `Map` 对象中所有元素的键,以插入顺序排列。
- {{jsxref("Map.prototype.set()")}}
- : 在 `Map` 对象中设置与指定的键 `key` 关联的值,并返回 `Map` 对象。
- {{jsxref("Map.@@iterator", "Map.prototype[@@iterator]()")}}

- : 返回一个新的迭代对象,其为一个包含 `Map` 对象中所有键值对的 `[key, value]` 数组,并以插入 `Map` 对象的顺序排列。

- {{jsxref("Map.prototype.keys()")}}
- : 返回一个新的迭代对象,其中包含 `Map` 对象中所有的键,并以插入 `Map` 对象的顺序排列。
- {{jsxref("Map.prototype.values()")}}
- : 返回一个新的迭代对象,其中包含 `Map` 对象中所有的值,并以插入 `Map` 对象的顺序排列。
- {{jsxref("Map.prototype.entries()")}}
- : 返回一个新的迭代对象,其为一个包含 `Map` 对象中所有键值对的 `[key, value]` 数组,并以插入 `Map` 对象的顺序排列。
- {{jsxref("Map.prototype.forEach()")}}
- : 以插入的顺序对 `Map` 对象中存在的键值对分别调用一次 `callbackFn`。如果给定了 `thisArg` 参数,这个参数将会是回调函数中 `this` 的值。
- [`Map.prototype[@@iterator]()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator)
- : 返回一个新的迭代器对象,其包含 `Map` 对象中所有元素 `[key, value]` 二元数组,以插入顺序排列。

## 示例

Expand Down Expand Up @@ -236,7 +287,7 @@ console.log(myMap.get(function () {})); // undefined,因为 keyFunc !== functi

### 将 NaN 作为 Map 的键

`NaN` 也可以作为 `Map` 对象的键。虽然 `NaN` 与任何值甚至与自己都不相等(`NaN !== NaN` 返回 true),但是因为所有的 `NaN` 的值都是无法区分的,所以下面的例子成立:
{{jsxref("NaN")}} 也可以作为键。虽然 `NaN` 与任何值甚至与自己都不相等(`NaN !== NaN` 返回 true),但是因为所有的 `NaN` 的值都是无法区分的,所以下面的例子成立:

```js
const myMap = new Map();
Expand All @@ -250,7 +301,7 @@ myMap.get(otherNaN);
// "not a number"
```

### 使用 for...of 方法迭代 Map
### 使用 for...of 迭代 Map

`Map` 可以使用 `for...of` 循环来实现迭代:

Expand Down Expand Up @@ -284,7 +335,7 @@ for (const [key, value] of myMap.entries()) {
// 1 = one
```

### 使用 forEach() 方法迭代 Map
### 使用 forEach() 迭代 Map

`Map` 也可以通过 {{jsxref("Map.prototype.forEach", "forEach()")}} 方法迭代:

Expand All @@ -296,20 +347,20 @@ myMap.forEach((value, key) => {
// 1 = one
```

### Map 与数组的关系
### Map 与数组对象的关系

```js
const kvArray = [
["key1", "value1"],
["key2", "value2"],
];

// 使用常规的 Map 构造函数可以将一个二维键值对数组转换成一个 Map 对象
// 使用常规的 Map 构造函数可以将一个二维的键值对数组转换成一个 Map 对象
const myMap = new Map(kvArray);

console.log(myMap.get("key1")); // "value1"

// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维的键值对数组
console.log(Array.from(myMap)); // 输出和 kvArray 相同的数组

// 更简洁的方法来做如上同样的事情,使用展开运算符
Expand Down Expand Up @@ -389,8 +440,7 @@ console.log(merged.get(3)); // three

## 参见

- A polyfill of `Map` is available in
[`core-js`](https://github.com/zloirock/core-js#map)
- [`core-js``Map` 的 polyfill](https://github.com/zloirock/core-js#map)
- {{jsxref("Set")}}
- {{jsxref("WeakMap")}}
- {{jsxref("WeakSet")}}

0 comments on commit 37c4a24

Please sign in to comment.