array.map
array.map
map()
方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回。
var numbers = [1, 5, 10, 15];
var doubles = numbers.map(function(x) {
return x * 2;
}
// doubles is now [2, 10, 20, 30]
// numbers is still [1, 5, 10, 15]
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt
// roots is now [1, 2, 3]
// numbers is still [1, 4, 9]
语法
var new_array = arr.map(function callback(currentValue, index, array) {
// Return element for new_array
}[, thisArg])
参数
callback
生成新数组元素的函数,使用三个参数:currentValue
callback
的第一个参数,数组中正在处理的当前元素。
返回值
一个新数组,每个元素都是回调函数的结果。
描述
map
方法会给原数组中的每个元素都按顺序调用一次 callback
函数。callback
每次执行后的返回值组合起来形成一个新数组,包括undefined。callback
函数只会在有值的索引上被调用;那些从来没被赋过值或者使用delete
删除的索引则不会被调用。
callback
函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
如果 thisArg
参数有值,则每次 callback
函数被调用的时候,this
都会指向 thisArg
参数上的这个对象。如果省略了 thisArg 参数,或者赋值为 null
或undefined
,则 this
指向全局对象 。
map
不修改调用它的原数组本身(当然可以在callback
执行时改变原数组)。
使用 map
方法处理数组时,数组元素的范围是在 callback
方法第一次调用之前就已经确定了。在map
方法执行的过程中:原数组中新增加的元素将不会被 callback
访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback
的值是 map
方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。
因在规范中定义了算法,如果被调用的映射数组是稀疏的,那么得到的数组也将保持相同的索引空白。
示例
求数组中每个元素的平方根
下面的代码创建了一个新数组,值为原数组中对应数字的平方根。
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt
// roots is now [1, 2, 3]
// numbers is still [1, 4, 9]
使用 map 重新格式化数组中的对象
以下代码将一个包含对象的数组用以创建一个包含新重新格式化对象的新数组。
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 is now [{1: 10}, {2: 20}, {3: 30}],
// kvArray is still:
// [{key: 1, value: 10},
// {key: 2, value: 20},
// {key: 3, value: 30}]
用一个仅有一个参数的函数来mapping一个数字数组
下面的代码表示了当函数需要一个参数时map的工作方式。这个参数会遍历原始数组中的元素。
var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
return num * 2;
}
// doubles is now [2, 8, 18]
// numbers is still [1, 4, 9]
一般的 map 方法
下面的例子演示如何在一个String
上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:
var map = Array.prototype.map;
var a = map.call('Hello World', function(x) {
return x.charCodeAt(0
}
// a now equals [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
querySelectorAll 应用
这个例子展示了如何迭代通过querySelectorAll
收集的对象的集合。在这种情况下,我们从屏幕上获得所有选择的选项并打印在控制台上:
var elems = document.querySelectorAll('select option:checked'
var values = Array.prototype.map.call(elems, function(obj) {
return obj.value;
}
更简单的是使用Array.from()
方法。
反转字符串
var str = '12345';
Array.prototype.map.call(str, function(x) {
return x;
}).reverse().join(''
// Output: '54321'
// Bonus: use '===' to test if original string was a palindrome
更简单的是使用String.split()
方法(请参阅使用split()反转字符串)。
使用技巧案例
通常情况下,map
方法中的callback
函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着map
只给callback
传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。
// Consider:
['1', '2', '3'].map(parseInt
// While one could expect [1, 2, 3]
// The actual result is [1, NaN, NaN]
// parseInt is often used with one argument, but takes two.
// The first is an expression and the second is the radix.
// To the callback function, Array.prototype.map passes 3 arguments:
// the element, the index, the array
// The third argument is ignored by parseInt, but not the second one,
// hence the possible confusion. See the blog post for more details
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` will also return a float or (resolved) exponential notation:
['1.1', '2.2e2', '3e300'].map(Number // [1.1, 220, 3e+300]
Polyfill
map
是在最近的 ECMA-262 标准中新添加的方法;所以一些旧版本的浏览器可能没有实现该方法。在那些没有原生支持 map
方法的浏览器中,你可以使用下面的 Javascript 代码来实现它。
所使用的算法正是 ECMA-262,第 5 版规定的。假定Object
, TypeError
, 和 Array
有他们的原始值。而且 callback.call
的原始值也是 Function.prototype.call
。
// 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;
};
}
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 5.1 (ECMA-262)The definition of 'Array.prototype.map' in that specification. | Standard | Initial definition. Implemented in JavaScript 1.6. |
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Array.prototype.map' in that specification. | Standard | |
ECMAScript Latest Draft (ECMA-262)The definition of 'Array.prototype.map' in that specification. | Living Standard | |
浏览器兼容性
Feature | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic Support | (Yes) | (Yes) | 1.5 | 9 | (Yes) | (Yes) |
Feature | Android | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
---|---|---|---|---|---|---|---|
Basic Support | (Yes) | (Yes) | (Yes) | 1 | (Yes) | (Yes) | (Yes) |