Array.prototype.map()

Сводка

Метод map() создаёт новый массив с результатом вызова указанной функции для каждого элемента массива.

Синтаксис

arr.map(callback[, thisArg])

Параметры

callback
Функция, создающая элемент в новом массиве, принимает три аргумента:
currentValue
Текущий элемент, создаваемый в массиве.
index
Индекс текущего обрабатываемого элемента в массиве.
array
Массив, по которому осуществляется проход.
thisArg
Необязательный параметр. Значение, используемое в качестве this при вызове функции callback.

Описание

Метод map вызывает переданную функцию callback один раз для каждого элемента, в порядке их появления и конструирует новый массив из результатов её вызова. Функция callback вызывается только для индексов массива, имеющих присвоенные значения; она не вызывается для индексов, значения по которым равны undefined, то есть, которые были удалены или которым значения никогда не присваивались.

Функция callback вызывается с тремя аргументами: значением элемента, индексом элемента и массивом, по которому осуществляется проход.

Если в метод map был передан параметр thisArg, при вызове callback он будет использоваться в качестве значения this. В противном случае в качестве значения this будет использоваться значение undefined. В конечном итоге, значение this, наблюдаемое из функции callback, определяется согласно обычным правилам определения this, видимого из функции.

Метод map не изменяет массив, для которого он был вызван (хотя функция callback может это делать).

Диапазон элементов, обрабатываемых методом map, устанавливается до первого вызова функции callback. Элементы, добавленные в массив после начала выполнения метода map, не будут посещены функцией callback. Если существующие элементы массива изменяются функцией callback, их значения, переданные в функцию, будут значениями на тот момент времени, когда метод map посетит их; удалённые элементы посещены не будут.

Примеры

Пример: отображение массива чисел на массив квадратных корней

Следующий код берёт массив чисел и создаёт новый массив, содержащий квадратные корни чисел из первого массива.

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// теперь roots равен [1, 2, 3], а numbers всё ещё равен [1, 4, 9]

Пример: отображение массива чисел с использованием функции, содержащей аргумент

Следующий код показывает, как работает отображение, когда функция требует один аргумент. Аргумент будет автоматически присваиваться каждому элементу массива, когда map проходит по оригинальному массиву.

var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
  return num * 2;
});
// теперь doubles равен [2, 8, 18], а numbers всё ещё равен [1, 4, 9]

Пример: обобщённое использование map

Этот пример показывает, как использовать map на объекте строки String для получения массива байт в кодировке ASCII, представляющего значения символов:

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]

Пример: обобщённое использование map вместе с querySelectorAll

Этот пример показывает, как пройтись по коллекции объектов, собранных с помощью querySelectorAll. В данном случае мы получаем все выбранные опции на экране и печатаем их в консоль:

var elems = document.querySelectorAll('select option:checked');
var values = [].map.call(elems, function(obj) {
  return obj.value;
});

Пример: использование map для переворачивания строки

var str = '12345';
[].map.call(str, function(x) {
  return x;
}).reverse().join('');

// Вывод: '54321'
// Бонус: используйте '===' для проверки того, является ли строка палиндромом

Пример: хитрый вариант использования

(навеяно этой записью в блоге)

Распространённой практикой является использование функции обратного вызова с одним аргументом (элемент, над которым производится операция). Некоторые функции также широко используется с одним аргументом, хотя они принимают дополнительные необязательные аргументы. Эти привычки могут привести к неожиданному поведению программы.

// Рассмотрим пример:
['1', '2', '3'].map(parseInt);
// Хотя ожидаемый результат вызова равен [1, 2, 3],
// в действительности получаем [1, NaN, NaN]

// Функция parseInt часто используется с одним аргументом, но она принимает два.
// Первый аргумент является выражением, а второй - основанием системы счисления.
// В функцию callback Array.prototype.map передаёт 3 аргумента:
// элемент, его индекс и сам массив.
// Третий аргумент игнорируется parseInt, но не второй, следовательно,
// возможна путаница. Смотрите запись в блоге для дополнительной информации.

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt);
// Результатом является массив чисел (как и ожидалось)

// Простейший способ добиться вышеозначенного поведения и избежать чувства "чё за!?":
['1', '2', '3'].map(Number); // [1, 2, 3]

Полифилл

Метод map был добавлен к стандарту ECMA-262 в 5-м издании; поэтому он может отсутствовать в других реализациях стандарта. Вы можете работать с ним, добавив следующий код в начало ваших скриптов, он позволяет использовать map в реализациях, которые не поддерживают этот метод. Этот алгоритм является точно тем, что описан в ECMA-262 5-го издания; он предполагает, что Object, TypeError и Array имеют свои первоначальные значения и что callback.call вычисляется в оригинальное значение Function.prototype.call.

// Шаги алгоритма ECMA-262, 5-е издание, 15.4.4.19
// Ссылка (en): http://es5.github.com/#x15.4.4.19
// Ссылка (ru): http://es5.javascript.ru/x15.4.html#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. Положим O равным результату вызова ToObject с передачей ему
    //    значения |this| в качестве аргумента.
    var O = Object(this);

    // 2. Положим lenValue равным результату вызова внутреннего метода Get
    //    объекта O с аргументом "length".
    // 3. Положим len равным ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. Если вызов IsCallable(callback) равен false, выкидываем исключение TypeError.
    // Смотрите (en): http://es5.github.com/#x9.11
    // Смотрите (ru): http://es5.javascript.ru/x9.html#x9.11
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. Если thisArg присутствует, положим T равным thisArg; иначе положим T равным undefined.
    if (arguments.length > 1) {
      T = thisArg;
    }

    // 6. Положим A равным новому масиву, как если бы он был создан выражением new Array(len),
    //    где Array является стандартным встроенным конструктором с этим именем,
    //    а len является значением len.
    A = new Array(len);

    // 7. Положим k равным 0
    k = 0;

    // 8. Пока k < len, будем повторять
    while (k < len) {

      var kValue, mappedValue;

      // a. Положим Pk равным ToString(k).
      //   Это неявное преобразование для левостороннего операнда в операторе in
      // b. Положим kPresent равным результату вызова внутреннего метода HasProperty
      //    объекта O с аргументом Pk.
      //   Этот шаг может быть объединён с шагом c
      // c. Если kPresent равен true, то
      if (k in O) {

        // i. Положим kValue равным результату вызова внутреннего метода Get
        //    объекта O с аргументом Pk.
        kValue = O[k];

        // ii. Положим mappedValue равным результату вызова внутреннего метода Call
        //     функции callback со значением T в качестве значения this и списком
        //     аргументов, содержащим kValue, k и O.
        mappedValue = callback.call(T, kValue, k, O);

        // iii. Вызовем внутренний метод DefineOwnProperty объекта A с аргументами
        // Pk, Описатель Свойства
        // { Value: mappedValue,
        //   Writable: true,
        //   Enumerable: true,
        //   Configurable: true }
        // и false.

        // В браузерах, поддерживающих Object.defineProperty, используем следующий код:
        // Object.defineProperty(A, k, {
        //   value: mappedValue,
        //   writable: true,
        //   enumerable: true,
        //   configurable: true
        // });

        // Для лучшей поддержки браузерами, используем следующий код:
        A[k] = mappedValue;
      }
      // d. Увеличим k на 1.
      k++;
    }

    // 9. Вернём A.
    return A;
  };
}

Спецификации

Спецификация Статус Комментарии
ECMAScript 5.1 (ECMA-262)
Определение 'Array.prototype.map' в этой спецификации.
Стандарт Изначальное определение. Реализована в JavaScript 1.6.
ECMAScript 6 (ECMA-262)
Определение 'Array.prototype.map' в этой спецификации.
Кандидат в рекомендации  

Совместимость с браузерами

Возможность Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базовая поддержка (Да) 1.5 (1.8) 9 (Да) (Да)
Возможность Android Chrome для Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базовая поддержка (Да) (Да) 1.0 (1.8) (Да) (Да) (Да)

Смотрите также

Метки документа и участники

Обновлялась последний раз: Mingun,
Скрыть боковую панель