1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
|
---
title: Array.prototype.map()
slug: Web/JavaScript/Reference/Global_Objects/Array/map
tags:
- Array
- ECMAScript5
- JavaScript
- Method
- Prototype
- polyfill
translation_of: Web/JavaScript/Reference/Global_Objects/Array/map
---
<div>{{JSRef}}</div>
<p><code><strong>map()</strong></code> 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。</p>
<div>{{EmbedInteractiveExample("pages/js/array-map.html")}}</div>
<h2 id="Syntax" name="Syntax">语法</h2>
<pre class="notranslate"><var>var new_array = arr</var>.map(function <var>callback(currentValue[, index[, array]]) {
// Return element for new_array </var>
<var>}</var>[, <var>thisArg</var>])</pre>
<h3 id="Parameters" name="Parameters">参数</h3>
<dl>
<dt><code>callback</code></dt>
<dd>生成新数组元素的函数,使用三个参数:
<dl>
<dt><code>currentValue</code></dt>
<dd><code>callback</code> 数组中正在处理的当前元素。</dd>
<dt><code>index</code>{{optional_inline}}</dt>
<dd><code>callback</code> 数组中正在处理的当前元素的索引。</dd>
<dt><code>array</code>{{optional_inline}}</dt>
<dd><code>map</code> 方法调用的数组。</dd>
</dl>
</dd>
<dt><code>thisArg</code>{{optional_inline}}</dt>
<dd>执行 <code>callback</code> 函数时值被用作<code>this</code>。</dd>
</dl>
<h3 id="返回值">返回值</h3>
<p>一个由原数组每个元素执行回调函数的结果组成的新数组。</p>
<h2 id="Description" name="Description">描述</h2>
<p><code>map</code> 方法会给原数组中的每个元素都按顺序调用一次 <code>callback</code> 函数。<code>callback</code> 每次执行后的返回值(包括 {{jsxref("undefined")}})组合起来形成一个新数组。 <code>callback</code> 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 <code>delete</code> 删除的索引则不会被调用。</p>
<p>因为<code>map</code>生成一个新数组,当你不打算使用返回的新数组却使用<code>map</code>是违背设计初衷的,请用<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">forEach</a></code>或者<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of">for-of</a></code>替代。你不该使用<code>map</code>: A)你不打算使用返回的新数组,或/且 B) 你没有从回调函数中返回值。</p>
<p><code>callback</code> 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。</p>
<p>如果 <code>thisArg</code> 参数提供给<code>map</code>,则会被用作回调函数的<code>this</code>值。否则{{jsxref("undefined")}}会被用作回调函数的<code>this</code>值。<code>this</code>的值最终相对于<code>callback</code>函数的可观察性是依据<a href="/en-US/docs/Web/JavaScript/Reference/Operators/this">the usual rules for determining the this seen by a function</a>决定的</p>
<p><code>map </code>不修改调用它的原数组本身(当然可以在 <code>callback</code> 执行时改变原数组)</p>
<p><code>map</code> 方法处理数组元素的范围是在 <code>callback</code> 方法第一次调用之前就已经确定了。调用<code>map</code>方法之后追加的数组元素不会被<code>callback</code>访问。如果存在的数组元素改变了,那么传给<code>callback</code>的值是<code>map</code>访问该元素时的值。在<code>map</code>函数调用后但在访问该元素前,该元素被删除的话,则无法被访问到。</p>
<p>根据规范中定义的算法,如果被map调用的数组是离散的,新数组将也是离散的保持相同的索引为空。</p>
<h2 id="Examples" name="Examples">示例</h2>
<h3 id="Example_Mapping_an_array_of_numbers_to_an_array_of_square_roots" name="Example:_Mapping_an_array_of_numbers_to_an_array_of_square_roots">求数组中每个元素的平方根</h3>
<p>下面的代码创建了一个新数组,值为原数组中对应数字的平方根。</p>
<pre class="brush: js notranslate">var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]</pre>
<h3 id="使用_map_重新格式化数组中的对象">使用 map 重新格式化数组中的对象</h3>
<p>以下代码使用一个包含对象的数组来重新创建一个格式化后的数组。</p>
<pre class="brush: js notranslate">var kvArray = [{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}];
var reformattedArray = kvArray.map(function(obj) {
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],
// kvArray 数组未被修改:
// [{key: 1, value: 10},
// {key: 2, value: 20},
// {key: 3, value: 30}]
</pre>
<h3 id="Example_Pluralizing_the_words_.28strings.29_in_an_array" name="Example:_Pluralizing_the_words_.28strings.29_in_an_array">使用一个包含一个参数的函数来mapping(构建)一个数字数组</h3>
<p>下面的代码表示了当函数需要一个参数时map的工作方式。当map循环遍历原始数组时,这个参数会自动被分配成数组中对应的每个元素。</p>
<pre class="brush: js notranslate">var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
return num * 2;
});
// doubles数组的值为: [2, 8, 18]
// numbers数组未被修改: [1, 4, 9]</pre>
<h3 id="Example_using_map_generically" name="Example:_using_map_generically">一般的<code>map</code> 方法</h3>
<p>下面的例子演示如何在一个 {{jsxref("String")}} 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:</p>
<pre class="brush: js notranslate">var map = Array.prototype.map
var a = map.call("Hello World", function(x) {
return x.charCodeAt(0);
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
</pre>
<h3 id="querySelectorAll_应用"><code>querySelectorAll 应用</code></h3>
<p>下面代码展示了如何去遍历用 <code>querySelectorAll </code>得到的动态对象集合。在这里,我们获得了文档里所有选中的选项,并将其打印:</p>
<pre class="brush: js notranslate">var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
return obj.value;
});
</pre>
<h3 id="使用技巧案例">使用技巧案例</h3>
<p><a class="external" href="http://www.wirfs-brock.com/allzh-cn/posts/166">(原文地址)</a></p>
<p>通常情况下,<code>map</code> 方法中的 <code>callback</code> 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 <code>map</code> 只给 <code>callback</code> 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。</p>
<p>考虑下例:</p>
<pre class="notranslate"><code>["1", "2", "3"].map(parseInt);</code></pre>
<p>我们期望输出 <code>[1, 2, 3]</code>, 而实际结果是 <code>[1, NaN, NaN]</code>.</p>
<p>parseInt 经常被带着一个参数使用, 但是这里接受两个。第一个参数是一个表达式而第二个是callback function的基, <code>Array.prototype.map</code> 传递3个参数:</p>
<ul>
<li>the element</li>
<li>the index</li>
<li>the array</li>
</ul>
<p>第三个参数被parseInt忽视了, <u>but not the second one</u>, 但不是第二个。因此可能出现混淆。下面是迭代步骤的简明示例:</p>
<pre class="notranslate"><code>// parseInt(string, radix) -> map(parseInt(value, index))
/* first iteration (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/* third iteration (index is 2): */ parseInt("3", 2); // NaN</code></pre>
<p>下面让我们来讨论解决方案:</p>
<pre class="notranslate"><code>function returnInt(element) {
return parseInt(element, 10);
}
['1', '2', '3'].map(returnInt); // [1, 2, 3]
// Actual result is an array of numbers (as expected)
// Same as above, but using the concise arrow function syntax
['1', '2', '3'].map( str => parseInt(str) );
// A simpler way to achieve the above, while avoiding the "gotcha":
['1', '2', '3'].map(Number); // [1, 2, 3]
// But unlike parseInt(), Number() will also return a float or (resolved) exponential notation:
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
// For comparison, if we use parseInt() on the array above:
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]</code></pre>
<p>一个map方法调用 parseInt 作为一个参数的等效输出运行如下:</p>
<pre class="notranslate"><code>var xs = ['10', '10', '10'];
xs = xs.map(parseInt);
console.log(xs); // 输出结果为</code>(3) [10, NaN, 2]<code>
// Actual result of 10,NaN,2 may be unexpected based on the above description.</code></pre>
<h3 id="Mapping_含_undefined的数组">Mapping 含 undefined的数组</h3>
<p>当返回undefined 或没有返回任何内容时:</p>
<pre class="notranslate"><code>var numbers = [1, 2, 3, 4];
var filteredNumbers = numbers.map(function(num, index) {
if(index < 3) {
return num;
}
});
//index goes from 0,so the filterNumbers are 1,2,3 and undefined.
// filteredNumbers is [1, 2, 3, undefined]
// numbers is still [1, 2, 3, 4]</code></pre>
<h2 id="Polyfill">Polyfill</h2>
<p><code>map</code> was added to the ECMA-262 standard in the 5th edition; as such it may not be present in all implementations of the standard. You can work around this by inserting the following code at the beginning of your scripts, allowing use of <code>map</code> in implementations which do not natively support it. This algorithm is exactly the one specified in ECMA-262, 5th edition, assuming {{jsxref("Object")}}, {{jsxref("TypeError")}}, and {{jsxref("Array")}} have their original values and that <code>callback.call</code> evaluates to the original value of <code>{{jsxref("Function.prototype.call")}}</code>.</p>
<pre class="notranslate"><code>// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback/*, thisArg*/) {
var T, A, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = arguments[1];
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}</code></pre>
<h2 id="Specifications">Specifications</h2>
<table>
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName('ES5.1', '#sec-15.4.4.19', 'Array.prototype.map')}}</td>
<td>{{Spec2('ES5.1')}}</td>
<td>Initial definition. Implemented in JavaScript 1.6.</td>
</tr>
<tr>
<td>{{SpecName('ES6', '#sec-array.prototype.map', 'Array.prototype.map')}}</td>
<td>{{Spec2('ES6')}}</td>
<td></td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-array.prototype.map', 'Array.prototype.map')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<table class="standard-table">
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<div>
<p>{{Compat("javascript.builtins.Array.map")}}</p>
</div>
<h2 id="See_also" name="See_also" style="margin-bottom: 20px; line-height: 30px;">相关链接</h2>
<ul>
<li>{{jsxref("Array.prototype.forEach()")}}</li>
<li>{{jsxref("Map")}} object</li>
<li>{{jsxref("Array.from()")}}</li>
</ul>
|