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 参数,或者赋值为 nullundefined,则 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 版规定的。假定ObjectTypeError, 和 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; }; }

规范

SpecificationStatusComment
ECMAScript 5.1 (ECMA-262)The definition of 'Array.prototype.map' in that specification.StandardInitial 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

浏览器兼容性

FeatureChromeEdgeFirefoxInternet ExplorerOperaSafari
Basic Support(Yes)(Yes)1.59(Yes)(Yes)

FeatureAndroidChrome for AndroidEdge mobileFirefox for AndroidIE mobileOpera AndroidiOS Safari
Basic Support(Yes)(Yes)(Yes)1(Yes)(Yes)(Yes)