--- title: Array.prototype.map() slug: Web/JavaScript/Reference/Global_Objects/Array/map translation_of: Web/JavaScript/Reference/Global_Objects/Array/map ---
Phương thức map()
giúp tạo ra một mảng mới với các phần tử là kết quả từ việc thực thi một hàm lên từng phần tử của mảng được gọi.
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Trả về element mới cho new_array }[, thisArg])
callback
currentValue
index
{{optional_inline}}array
{{optional_inline}}map
thisArg
{{optional_inline}}this
bên trong callback
.Một mảng mới với phần tử là kết quả của hàm callback
.
map
sẽ gọi hàm callback
lên từng phần tử của mảng, theo thứ tự trong mảng, và tạo ra một mảng mới chứa các phần tử kết quả. callback
chỉ được gọi cho những vị trí index của mảng được gán giá trị, bao gồm cả undefined. Nó không được gọi cho những vị trí index trống (là các index của mảng chưa bao giờ được khởi tạo, bao gồm index chưa được gán giá trị hoặc đã bị xóa bằng delete
).
callback
được gọi với ba tham số: giá trị của phần tử, index của phần tử, và chính mảng đang được gọi.
Nếu tham số thisArg
được truyền cho map
, nó sẽ được gán cho từ khóa this
trong hàm callback. Nếu không, giá trị undefined
sẽ được dùng cho this
với strict mode. Tóm lại, giá trị của từ khóa this
trong hàm callback
được xác định tuân theo các quy tắc thông thường để xác định this
trong một hàm.
map
không làm thay đổi mảng ban đầu mà nó được gọi (mặc dù mảng ban đầu vẫn có thể bị thay đổi trong hàm callback
).
Các phần tử được map()
xử lý được xác định từ đầu trước khi callback
được gọi lần đầu tiên. Những phần tử mới được thêm vào sau khi map
đã bắt đầu chạy sẽ không được callback
gọi đến. Trong lúc map
đang chạy, nếu những phần tử hiện tại của mảng bị thay đổi, thì giá trị mới của chúng sẽ được truyền cho hàm callback
ngay tại thời điểm callback
chạy qua. Những phần tử bị xóa sau khi map
đã bắt đầu và trước khi đến lượt nó cũng sẽ bị bỏ qua.
Do thuật toán đã quy định trong đặc tả, nếu như mảng ban đầu đã có sẵn những lỗ trống (index rỗng) thì sau khi được gọi với map
, mảng đầu ra cũng sẽ có những index rỗng như mảng ban đầu.
Đoạn code sau sẽ map một mảng các số thành một mảng mới chứa giá trị là căn bậc hai của các số trong mảng ban đầu.
var numbers = [1, 4, 9]; var roots = numbers.map(Math.sqrt); // mảng roots: [1, 2, 3] // mảng numbers vẫn là: [1, 4, 9]
Đoạn code sau sẽ xử lý một mảng các object và trả về một mảng mới chứa các object đã được format lại:
var kvArray = [{key: 1, value: 10}, {key: 2, value: 20}, {key: 3, value: 30}]; var reformattedArray = kvArray.map(obj =>{ var rObj = {}; rObj[obj.key] = obj.value; return rObj; }); // reformattedArray bây giờ là: [{1: 10}, {2: 20}, {3: 30}], // kvArray vẫn là: // [{key: 1, value: 10}, // {key: 2, value: 20}, // {key: 3, value: 30}]
Đoạn code sau minh họa cách thức map hoạt động khi callback
có sử dụng tham số. Tham số này sẽ có giá trị của từng phần tử của mảng ban đầu khi map duyệt qua mảng này.
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
một cách độc lậpVí dụ sau sẽ minh họa cách dùng map
lên một {{jsxref("String")}} một cách độc lập để trả về một mảng các mã byte trong bảng ASCII đại diện cho từng ký tự của chuỗi.
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]
map
với kết quả của querySelectorAll
Ví dụ sau minh họa cách duyệt qua một tập các object (không phải mảng) trả về từ querySelectorAll
. Trong trường hợp này, chúng ta sẽ in ra giá trị của tất cả các option được chọn (của các thẻ select) lên console:
var elems = document.querySelectorAll('select option:checked'); var values = Array.prototype.map.call(elems, function(obj) { return obj.value; });
Cách trên có thể được giải quyết đơn giản hơn bằng cách sử dụng {{jsxref("Array.from()")}}.
Chúng ta hay dùng map với hàm một tham số (tham số đó chính là phần tử được duyệt). Và có một số hàm đặc biệt cũng thường được gọi với một tham số, mặc dù chúng có thêm tham số phụ không bắt buộc. Những thói quen này có thể dẫn đến những kết quả ngoài dự đoán.
// Lấy ví dụ: ['1', '2', '3'].map(parseInt); // Bạn sẽ nghĩ rằng kết quả là [1, 2, 3] // Nhưng kết quả lại là [1, NaN, NaN] // parseInt thường được dùng với 1 tham số, nhưng nó có thể nhận đến 2 tham số. // Tham số thứ nhất là một biểu thức giá trị và tham số thứ hai là cơ số // Với hàm callback của Array.prototype.map, nó sẽ nhận vào 3 tham số: // phần tử, index, mảng ban đầu // Tham số thứ 3 sẽ được parseInt bỏ qua, nhưng tham số thứ 2 lại có vai trò // và sẽ dẫn đến kết quả không mong muốn. // Sau đây là kết quả thực thi tại từng phần tử: // parseInt(string, radix) -> map(parseInt(value, index)) // lần chạy 1 (index là 0): parseInt('1', 0) // cho ta parseInt('1', 0) -> 1 // lần chạy 2 (index là 1): parseInt('2', 1) // cho ta parseInt('2', 1) -> NaN // lần chạy 3 (index là 2): parseInt('3', 2) // cho ta parseInt('3', 2) -> NaN function returnInt(element) { return parseInt(element, 10); } ['1', '2', '3'].map(returnInt); // [1, 2, 3] // Thông qua hàm returnInt, kết quả trả về là mảng các số (như mong muốn) // Tương tự như trên, nhưng sử dụng một hàm arrow ['1', '2', '3'].map( str => parseInt(str) ); // Một cách nữa đơn giản hơn nhưng tránh được vấn đề hàm nhiều tham số: ['1', '2', '3'].map(Number); // [1, 2, 3] // Tuy nhiên khác với `parseInt`, cách này sẽ giữ lại định dạng // số thập phân hoặc ký hiệu mũ từ chuỗi ban đầu ['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
Một ví dụ khác khi gọi map
với parseInt
là tham số cho callback:
var xs = ['10', '10', '10']; xs = xs.map(parseInt); console.log(xs); // Kết quả trả về là 10,NaN,2 như đã lý giải ở trên.
map()
chỉ được thêm vào đặc tả ECMA-262 với phiên bản lần thứ 5; cho nên nó có thể không tồn tại trong một số hiện thực (implementation) của đặc tả. Bạn có thể work around bằng cách thêm vào đoạn code bên dưới vào đầu script của bạn, cho phép sử dụng hàm map
tại những nơi mà nó không được hỗ trợ sẵn. Giải thuật trong polyfill này tương đương với hàm map
được mô tả trong đặc tả ECMA-262, 5th edition với điều kiện {{jsxref("Object")}}, {{jsxref("TypeError")}}, và {{jsxref("Array")}} không bị thay đổi và callback.call
chính là hàm được định nghĩa trong {{jsxref("Function.prototype.call")}}
.
Ghi chú bản dịch: những đoạn comment trong code bên dưới trích từ đặc tả ECMA nên sẽ giữ nguyên.
// 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;
};
}
Đặc tả | Trạng thái | Ghi chú |
---|---|---|
{{SpecName('ES5.1', '#sec-15.4.4.19', 'Array.prototype.map')}} | {{Spec2('ES5.1')}} | Định nghĩa lần đầu. Hiện thực trong JavaScript 1.6. |
{{SpecName('ES6', '#sec-array.prototype.map', 'Array.prototype.map')}} | {{Spec2('ES6')}} | |
{{SpecName('ESDraft', '#sec-array.prototype.map', 'Array.prototype.map')}} | {{Spec2('ESDraft')}} |
{{Compat("javascript.builtins.Array.map")}}