Skip to content

Commit 53f9434

Browse files
authored
Merge pull request javascript-tutorial#589 from bemself/map-85e67eb
1-js/05-data-types/07-map-set/
2 parents ecc9369 + 5c94ca7 commit 53f9434

1 file changed

Lines changed: 98 additions & 98 deletions

File tree

Lines changed: 98 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11

22
# Map and Set(映射和集合)
33

4-
我们已经了解过以下复杂数据结构:
4+
我们已经了解了以下复杂的数据结构:
55

6-
- 存储键集合的对象.
7-
- 存储有序集合的数组.
6+
- 存储键集合的对象
7+
- 存储有序集合的数组
88

9-
但在我们的开发中并它不能满足需求。这就是为什么 `Map``Set` 也存在的原因.
9+
但现实生活中远不止这些。这就是为什么 `Map``Set` 也存在的原因
1010

1111
## Map
1212

13-
[Map](mdn:js/Map) 是一个含有数据的键的集合, 跟普通的 `Object` 一样. 但是它们最大的差别是 `Map` 允许键是任何类型
13+
[Map](mdn:js/Map) 是一个带键的数据集合,跟普通的 `Object` 一样 但是它们最大的差别是 `Map` 允许键(key)是任何类型
1414

15-
它的方法和属性如下:
15+
它的方法和属性如下
1616

17-
- `new Map()` - 创建一个空集合
18-
- `map.set(key, value)` - 存储含有值的键
19-
- `map.get(key)` - 根据键来返回值, 如果 `key` 不在 `map` 里将会返回 `undefined`
20-
- `map.has(key)` - 如果 `key` 存在则返回 `true`, 否则返回 `false`
21-
- `map.delete(key)` - 根据键来删除值。
22-
- `map.clear()` - 清空集合。
23-
- `map.size` - 返回当前全部元素的数量
17+
- `new Map()` - 创建 `Map`
18+
- `map.set(key, value)` - 存储键值对
19+
- `map.get(key)` 根据键来返回值如果 `key` 不在 `map` 里将会返回 `undefined`
20+
- `map.has(key)` 如果 `key` 存在则返回 `true`否则返回 `false`
21+
- `map.delete(key)` 根据键来删除值。
22+
- `map.clear()` 清空集合。
23+
- `map.size` - 返回当前的元素个数
2424

25-
举个例子:
25+
举个例子
2626

2727
```js run
2828
let map = new Map();
@@ -31,80 +31,79 @@ map.set('1', 'str1'); // 字符串键
3131
map.set(1, 'num1'); // 数字键
3232
map.set(true, 'bool1'); // 布尔值键
3333

34-
// 还记得常规对象吗? 它会把键转化为字符串
35-
// Map 会保持类型, 所以下面这两个结果不同:
34+
// 还记得普通的 Object 对象吗? 它会将键转化为字符串
35+
// Map 则会保留类型,所以下面这两个结果不同
3636
alert( map.get(1) ); // 'num1'
3737
alert( map.get('1') ); // 'str1'
3838

3939
alert( map.size ); // 3
4040
```
4141

42-
如我们所见,与对象不同,键(key)不会转换为字符串。键可以是任何类型。
42+
如我们所见,与对象不同,键不会转换为字符串。键可以是任何类型。
4343

4444
**Map 还可以使用对象作为键**
4545

46-
例如:
46+
例如
4747

4848
```js run
4949
let john = { name: "John" };
5050

51-
// 我们先存储每个游客的来访次数
51+
// 存储每个用户的来访次数
5252
let visitsCountMap = new Map();
5353

54-
// john 已经在集合里了
54+
// john 是集合中的键
5555
visitsCountMap.set(john, 123);
5656

5757
alert( visitsCountMap.get(john) ); // 123
5858
```
5959

6060
使用对象作为键是 `Map` 最值得注意和重要的功能之一。对于字符串键,`Object`(普通对象)能正常使用,但对于对象键则不能。
6161

62-
我们来尝试一下:
62+
我们来尝试一下
6363

6464
```js run
6565
let john = { name: "John" };
6666

67-
let visitsCountObj = {}; // try to use an object
67+
let visitsCountObj = {}; // 尝试使用对象
6868

69-
visitsCountObj[john] = 123; // try to use john object as the key
69+
visitsCountObj[john] = 123; // 尝试将 john 对象作为键
7070

7171
*!*
72-
// That's what got written!
72+
// 是写成了这样!
7373
alert( visitsCountObj["[object Object]"] ); // 123
7474
*/!*
7575
```
7676

77-
`visitsCountObj` 是一个普通对象的时候, 它会转化所有的键, `john` 被转化为字符串, 所以我们得到字符键 `"[object Object]"`. 很明显这不是我们要的结果.
77+
因为 `visitsCountObj` 是一个普通对象,它会将所有的键如 `john` 转换为字符串,所以我们得到字符键 `"[object Object]"`。这显然不是我们想要的结果。
7878

7979
```smart header="`Map` 是怎么比较键的?"
8080

81-
为了测试键的一致性, `Map` 使用 [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero)算法。 它大致上使用严格全等 `===`, 但不同的是 `NaN` 被看成是等于 `NaN`。 所以 `NaN` 也可以被用作键.
81+
`Map` 使用 [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero)算法来比较键是否相等。 它和严格等于 `===` 差不多,但区别是 `NaN` 被看成是等于 `NaN`。 所以 `NaN` 也可以被用作键
8282

8383
这个算法不能被改变或者自定义。
8484

8585
```
8686
8787
````smart header="链式调用"
8888
89-
每一次调用 `map.set` 都会返回集合本身, 所以我们可以使用:
89+
每一次调用 `map.set` 都会返回集合本身,所以我们可以”连续“调用:
9090
9191
```js
9292
map.set('1', 'str1')
9393
.set(1, 'num1')
9494
.set(true, 'bool1');
9595
```
96-
````
9796

9897

9998
## `Map` 迭代
10099

101-
如果要在 `map` 里使用循环, 可以使用以下三个方法:
100+
如果要在 `map` 里使用循环可以使用以下三个方法
102101

103-
- `map.keys()` - 返回键名的遍历器,
104-
- `map.values()` - 返回键值的遍历器,
105-
- `map.entries()` - 返回实体 `[key, value]` 的遍历器,默认在 `for..of` 中使用。
102+
- `map.keys()` 返回键名的遍历器,
103+
- `map.values()` 返回键值的遍历器,
104+
- `map.entries()` 返回实体 `[key, value]` 的遍历器,默认在 `for..of` 中使用。
106105

107-
例如:
106+
例如
108107

109108
```js run
110109
let recipeMap = new Map([
@@ -118,34 +117,34 @@ for (let vegetable of recipeMap.keys()) {
118117
alert(vegetable); // cucumber, tomatoes, onion
119118
}
120119

121-
// 迭代值 (amounts)
120+
// 迭代值amounts
122121
for (let amount of recipeMap.values()) {
123122
alert(amount); // 500, 350, 50
124123
}
125124

126125
// 迭代 [key, value] 对
127126
for (let entry of recipeMap) { // 效果跟 recipeMap.entries() 相同
128-
alert(entry); // cucumber,500 (and so on)
127+
alert(entry); // cucumber,500(以此类推)
129128
}
130129
```
131130

132-
```smart header="按顺序插入"
131+
```smart header="使用插入顺序"
133132
134-
迭代的顺序与插入键的顺序相同。 `Map` 会保持相同的顺序,不像普通 `Object` 不保证顺序.
133+
迭代的顺序与键的插入顺序相同。不同于普通的 `Object`,`Map` 会保留键的插入顺序。
135134
```
136135

137-
除此之外, `Map` 有个内建 `forEach` 方法, 跟 `Array` 一样:
136+
除此之外`Map` 有个内置的 `forEach` 方法`Array` 类似:
138137

139138
```js
140-
// 对每个 (key, value) 对运行 forEach 函数
139+
// 对每个 (key, value) 键值对运行 forEach 函数
141140
recipeMap.forEach( (value, key, map) => {
142-
alert(`${key}: ${value}`); // cucumber: 500 etc
141+
alert(`${key}: ${value}`); // cucumber: 500
143142
});
144143
```
145144

146-
## Object.entries: 把对象转化为 `map`
145+
## Object.entries把对象转化为 `map`
147146

148-
`Map` 被创建之后, 我们可以传入带有键值对的数组 (或其它可迭代的对象) 来进行初始化, 像这样:
147+
创建 `Map` 后,我们可以传入带有键值对的数组 (或其它可迭代的对象) 来进行初始化像这样
149148

150149
```js run
151150
// 包含 [key, value] 对的数组
@@ -158,7 +157,7 @@ let map = new Map([
158157
alert( map.get('1') ); // str1
159158
```
160159

161-
如果我们有个纯对象, 并且想利用这个纯对象来创建 `Map`, 可以使用内建方法 [Object.entries(obj)](mdn:js/Object/entries),它返回一个具有相同格式的并且带有键值对的数组对象
160+
如果我们想从一个已有的 plain object 来创建 `Map`,可以使用内置方法 [Object.entries(obj)](mdn:js/Object/entries)它返回一个对象的键值对数组,该数组符合 `Map` 集合的格式
162161

163162
所以可以像下面这样利用一个对象来创建 `map`
164163

@@ -180,9 +179,9 @@ alert( map.get('name') ); // John
180179

181180
## Object.fromEntries:把 `map` 转化为对象
182181

183-
我们刚刚已经利用 `Object.entries(obj)` 把一个纯对象转化成 `Map`
182+
我们刚刚已经利用 `Object.entries(obj)` 把一个 plain object 转化成 `Map`
184183

185-
`Object.fromEntries` 方法的作用是相反的: 给定一个具有 `[key, value]` 对的数组, 它会根据给定数组生成对象:
184+
`Object.fromEntries` 方法的作用是相反的给定一个具有 `[key, value]` 对的数组它会根据给定数组生成对象
186185

187186
```js run
188187
let prices = Object.fromEntries([
@@ -196,11 +195,11 @@ let prices = Object.fromEntries([
196195
alert(prices.orange); // 2
197196
```
198197

199-
我们可以使用 `Object.fromEntries` 从 `Map` 中得到一个纯对象
198+
我们可以使用 `Object.fromEntries``Map` 中得到一个 plain object
200199

201-
例如, 我们存了数据在 `Map` 中, 但是我们需要把它转给需要纯对象的第三方代码
200+
例如,我们存了数据在 `Map`,但是我们需要把它传给需要 plain object 的第三方代码
202201

203-
我们来开始:
202+
我们来开始
204203

205204
```js run
206205
let map = new Map();
@@ -209,43 +208,43 @@ map.set('orange', 2);
209208
map.set('meat', 4);
210209

211210
*!*
212-
let obj = Object.fromEntries(map.entries()); // make a plain object (*)
211+
let obj = Object.fromEntries(map.entries()); // 生成一个 plain object (*)
213212
*/!*
214213

215-
// done!
214+
// 完成!
216215
// obj = { banana: 1, orange: 2, meat: 4 }
217216

218217
alert(obj.orange); // 2
219218
```
220219

221-
调用 `map.entries()` 将返回含有键值对的数组, 这刚好是 `Object.fromEntries` 所需要的格式.
220+
调用 `map.entries()` 将返回含有键值对的数组这刚好是 `Object.fromEntries` 所需要的格式
222221

223-
我们可以把带 `(*)` 这一行变得更短:
222+
我们可以把带 `(*)` 的这一行写得更短:
224223

225224
```js
226-
let obj = Object.fromEntries(map); // omit .entries()
225+
let obj = Object.fromEntries(map); // 省掉 .entries()
227226
```
228227

229-
上面的代码作用也是一样的, 因为 `Object.fromEntries` 需要一个可迭代对象作为参数, 而一定是数组. `map` 的标准迭代会返回跟 `map.entries()` 一样的键值对. 所以我们可以获得一个与 `Map` 一样具有键值对的纯对象
228+
上面的代码作用也是一样的因为 `Object.fromEntries` 需要一个可迭代对象作为参数,而不一定是数组。`map` 的标准迭代会返回跟 `map.entries()` 一样的键值对所以我们可以获得一个与 `map` 一样具有键值对的 plain object
230229

231230
## Set
232231

233-
`Set` 是一个特别的类型集合 - "值的集合" (没有键 ),它的每一个值只出现一次。
232+
`Set` 是一个特殊类型的集合 - ”值的集合 (没有键 ),它的每一个值只出现一次。
234233

235-
它的主要方法如下:
234+
它的主要方法如下
236235

237-
- `new Set(iterable)` - 创建一个 `set`, 如果提供一个 `iterable` 对象(通常是数组),将会从数组里面复制值到 `set` 里面去
238-
- `set.add(value)` - 添加一个值,返回 set 本身
239-
- `set.delete(value)` - 删除值, 如果 `value` 在调用的时候值存在则返回 `true` ,否则返回 `false`。
240-
- `set.has(value)` - 如果 `value` 存在 set 里面则返回 `true`, 否则返回 `false`。
241-
- `set.clear()` - 移除 set 里面的所有成员
242-
- `set.size` - 返回元素数量
236+
- `new Set(iterable)` 创建一个 `set`,如果提供了一个 `iterable` 对象(通常是数组),将会从数组里面复制值到 `set`
237+
- `set.add(value)` 添加一个值,返回 set 本身
238+
- `set.delete(value)` 删除值如果 `value` 在调用的时候存在则返回 `true` ,否则返回 `false`
239+
- `set.has(value)` 如果 `value` set 中,返回 `true`否则返回 `false`
240+
- `set.clear()` - 清空集合
241+
- `set.size` - 返回元素的个数
243242

244-
它的主要特点是重复使用同一个值调用 `set.add(value)` 并不会发生什么改变。 这就是 `Set` 里面的每一个值只出现一次的原因。
243+
它的主要特点是重复使用同一个值调用 `set.add(value)` 并不会发生什么改变。这就是 `Set` 里面的每一个值只出现一次的原因。
245244

246-
例如, 我们有游客来访, 需要记住他们每一个人. 但是已经访问过的旅客会导致重复记录. 每个游客必须只被 "counted" 一次.
245+
例如,我们有客人来访,想记住他们每一个人。但是已经访问过的客人不应重复记录。每个访客必须只被“计数”一次。
247246

248-
`Set` 可以帮助我们解决这个问题:
247+
`Set` 可以帮助我们解决这个问题
249248

250249
```js run
251250
let set = new Set();
@@ -254,75 +253,76 @@ let john = { name: "John" };
254253
let pete = { name: "Pete" };
255254
let mary = { name: "Mary" };
256255

257-
// visits, 一些访客来访好几次
256+
// visits一些访客来访好几次
258257
set.add(john);
259258
set.add(pete);
260259
set.add(mary);
261260
set.add(john);
262261
set.add(mary);
263262

264-
// set 只保留那个游客第一次来的次数
263+
// set 只保留单一值
265264
alert( set.size ); // 3
266265

267266
for (let user of set) {
268-
alert(user.name); // John (然后 Pete 最后是 Mary)
267+
alert(user.name); // John (然后 Pete Mary)
269268
}
270269
```
271270

272-
使用 `Set` 的场景可以是一个用户数组, 并且每次插入的时候检查重复的的代码也可以使用 [arr.find](mdn:js/Array/find)。但是这样会是性能变的更差,因为这个方法会遍历整个数组来检查每个元素。 `Set` 有更好的内部优化 - 独一无二的检查.
271+
替代 `Set` 的场景可以是一个用户数组,[arr.find](mdn:js/Array/find) 在每次插入时检查是否重复。但是这样会使性能变差,因为这个方法会遍历整个数组来检查每个元素。`Set` 内部对唯一性检查进行了更好的优化。
273272

274273
## Set 迭代
275274

276-
我们可以在 `Set` 中使用 `for..of``forEach` 它们两者之一来循环 :
275+
我们可以使用 `for..of``forEach` 来循环 `Set` 集合:
277276

278277
```js run
279278
let set = new Set(["oranges", "apples", "bananas"]);
280279

281280
for (let value of set) alert(value);
282281

283-
// 跟 forEach 方法相同:
282+
// 跟 forEach 方法相同
284283
set.forEach((value, valueAgain, set) => {
285284
alert(value);
286285
});
287286
```
288287

289-
注意到一件有趣的事情。`forEach` 的回调函数有三个参数:一个 `value`,然后是 *相同值* `valueAgain`,最后才是目标对象本身,的确,相同的值在参数里出现了两次。
288+
注意一件有趣的事情`forEach` 的回调函数有三个参数:一个 `value`,然后是*相同值* `valueAgain`最后才是目标对象。没错,相同的值在参数里出现了两次。
290289

291-
那是为了兼容 `Map` 在回调里传入 `forEach` 函数后有三个参数。 当然这看起来有点奇怪。但是这对在特定情况下比如使用 `Set` 代替 `Map`的时候有帮助,反之亦然。
290+
这是为了兼容 `Map` 在回调里传入 `forEach` 函数后有三个参数。当然这看起来有点奇怪。但是这对在特定情况下比如使用 `Set` 代替 `Map` 的时候有帮助,反之亦然。
292291

293-
类似于 `Map`,在 `Set`里用于迭代的方法也被支持:
294-
- `set.keys()` - 返回一个包含值的可迭代对象,
295-
- `set.values()` - 跟 `set.keys()` 作用相同, 为了兼容 `Map`,
296-
- `set.entries()` - 返回一个包含 `[value, value]` 对的可迭代对象 , 它的存在也是为了兼容 `Map`.
292+
`Map` 中用于迭代的方法在 `Set` 中也同样支持:
293+
294+
- `set.keys()` - 返回一个包含值的可迭代对象,
295+
- `set.values()` - 跟 `set.keys()` 作用相同,为了兼容 `Map`
296+
- `set.entries()` - 返回一个包含 `[value, value]` 对的可迭代对象,它的存在也是为了兼容 `Map`
297297

298298
## 总结
299299

300-
`Map` - 是一个键值集合.
300+
`Map` - 是一个键值对集合。
301301

302-
方法和属性如下:
302+
方法和属性如下
303303

304-
- `new Map([iterable])` - 创建空的 map, 可选的带有 `[key,value]` 对的`iterable` (例如数组) 对象来进行初始化 。
305-
- `map.set(key, value)` - 存储对应的键值。
306-
- `map.get(key)` - 根据键来返回值, 如果键不存在 map 里就返回 `undefined`。
307-
- `map.has(key)` - 如果 `key` 存在则返回 `true` , 否则返回 `false`。
308-
- `map.delete(key)` - 删除指定键值
309-
- `map.clear()` - 清空 map 。
310-
- `map.size` - 返回当前全部元素的数量
304+
- `new Map([iterable])` 创建空的 map,可选择带有 `[key,value]` 对的 `iterable`例如数组对象来进行初始化 。
305+
- `map.set(key, value)` 存储对应的键值。
306+
- `map.get(key)` 根据键来返回值如果键不存在 map 里就返回 `undefined`
307+
- `map.has(key)` 如果 `key` 存在则返回 `true`否则返回 `false`
308+
- `map.delete(key)` - 删除指定键的值
309+
- `map.clear()` 清空 map 。
310+
- `map.size` - 返回当前全部元素的个数
311311

312-
跟普通对象 `Object` 最大的不同点是:
312+
跟 plain object `Object` 最大的不同点是
313313

314-
- 任何键,对象都可以被用作它的键
315-
- 有额外的方法, 和 `size` 属性。
314+
- 任何键、对象都可以作为键
315+
- 有其他的便捷方法,如 `size` 属性。
316316

317-
`Set` - 是一个独一无二的值的集合.
317+
`Set` - 是一组唯一值的集合。
318318

319-
方法和属性:
319+
方法和属性
320320

321-
- `new Set([iterable])` - 创建空的 set , 可选的带有 `iterable` (例如数组) 对象来进行初始化。
322-
- `set.add(value)` - 添加一个 value(如果存在则什么也不做), 返回 set 本身。
323-
- `set.delete(value)` - 删除 value , 如果在调用的时候存在则返回 `true`, 否则返回 `false`。
324-
- `set.has(value)` - 如果则返回 `true`, 否则返回 `false`。
325-
- `set.clear()` - 清空 set
326-
- `set.size` - 返回当前全部元素的数量
321+
- `new Set([iterable])` 创建空的 set,可选的 `iterable`例如数组对象来进行初始化。
322+
- `set.add(value)` - 添加一个值(如果值不存在),返回 set 本身。
323+
- `set.delete(value)` - 删除值,如果 `value` 在调用的时候存在则返回 `true`否则返回 `false`
324+
- `set.has(value)` - 如果值在集合中,则返回 `true`否则返回 `false`
325+
- `set.clear()` - 清空集合
326+
- `set.size` - 返回当前全部元素的个数
327327

328-
在 `Map` 和 `Set` 里迭代总是按照插入的顺序来执行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其顺序来获取元素。
328+
`Map``Set` 中迭代总是按照插入的顺序来执行的,所以我们不能说这些集合是无序的,但是我们不能对元素进行重新排序,也不能直接按其顺序来获取元素。

0 commit comments

Comments
 (0)