← Назад к вопросам
Реализовать собственный метод Array.prototype.map
2.3 Middle🔥 181 комментариев
#JavaScript Core
Условие
Напишите собственную реализацию метода map для массивов, не используя встроенный Array.prototype.map.
Требования
-
Функция myMap(arr, callback) должна принимать:
- arr - исходный массив
- callback - функция преобразования (element, index, array)
-
Возвращать новый массив с результатами вызова callback для каждого элемента
-
Не должна изменять исходный массив
Примеры
const numbers = [1, 2, 3, 4, 5];
myMap(numbers, x => x * 2);
// Результат: [2, 4, 6, 8, 10]
myMap(numbers, (x, i) => x + i);
// Результат: [1, 3, 5, 7, 9]
myMap(["a", "b", "c"], (char, i) => char.repeat(i + 1));
// Результат: ["a", "bb", "ccc"]
Бонус
Добавьте третий параметр thisArg для задания контекста callback.
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Реализация Array.prototype.map
Понимание map
Метод map — один из столпов функционального программирования в JavaScript. Он создаёт новый массив, применяя функцию преобразования к каждому элементу исходного массива. Понимание внутреннего устройства помогает глубже освоить работу с данными.
Базовая реализация
function myMap(arr, callback) {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i], i, arr));
}
return result;
}
С поддержкой thisArg
function myMap(arr, callback, thisArg) {
const result = [];
for (let i = 0; i < arr.length; i++) {
// Вызываем callback с заданным контекстом
result.push(callback.call(thisArg, arr[i], i, arr));
}
return result;
}
TypeScript версия
function myMap<T, U>(
arr: T[],
callback: (element: T, index: number, array: T[]) => U,
thisArg?: any
): U[] {
const result: U[] = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback.call(thisArg, arr[i], i, arr));
}
return result;
}
Версия с поддержкой разреженных массивов
function myMap(arr, callback, thisArg) {
// Спецификация ECMAScript требует игнорировать пустые слоты
const result = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
// Проверяем, что элемент существует
if (i in arr) {
result[i] = callback.call(thisArg, arr[i], i, arr);
}
}
return result;
}
Полная спецификация-совместимая версия
function myMap(arr, callback, thisArg) {
// Преобразуем в объект (массив может быть array-like)
if (arr == null) {
throw new TypeError('arr is null or undefined');
}
const O = Object(arr);
const len = parseInt(O.length, 10) || 0;
if (typeof callback !== 'function') {
throw new TypeError(`${callback} is not a function`);
}
const A = new Array(len);
for (let k = 0; k < len; k++) {
if (k in O) {
const mappedValue = callback.call(thisArg, O[k], k, O);
A[k] = mappedValue;
}
}
return A;
}
Примеры использования
// Базовый пример
const numbers = [1, 2, 3, 4, 5];
const doubled = myMap(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// С индексом
const indexed = myMap(numbers, (x, i) => x + i);
console.log(indexed); // [1, 3, 5, 7, 9]
// Со строками
const chars = ["a", "b", "c"];
const repeated = myMap(chars, (char, i) => char.repeat(i + 1));
console.log(repeated); // ["a", "bb", "ccc"]
// С объектами
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 35 }
];
const names = myMap(users, (user) => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]
// С контекстом (thisArg)
const multiplier = { factor: 10 };
const scaled = myMap([1, 2, 3], function(x) {
return x * this.factor;
}, multiplier);
console.log(scaled); // [10, 20, 30]
// Цепирование
const result = myMap([1, 2, 3], x => x * 2)
.reduce((sum, x) => sum + x, 0);
console.log(result); // 12
Версия как метод прототипа
if (!Array.prototype.myMap) {
Array.prototype.myMap = function(callback, thisArg) {
if (this == null) {
throw new TypeError('Array.prototype.myMap called on null or undefined');
}
const O = Object(this);
const len = parseInt(O.length) || 0;
if (typeof callback !== 'function') {
throw new TypeError(`${callback} is not a function`);
}
const A = new Array(len);
for (let k = 0; k < len; k++) {
if (k in O) {
A[k] = callback.call(thisArg, O[k], k, O);
}
}
return A;
};
}
// Использование
const nums = [1, 2, 3];
const mapped = nums.myMap(x => x * 2);
console.log(mapped); // [2, 4, 6]
Сравнение реализаций
// Наивная версия
function naiveMap(arr, cb) {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(cb(arr[i], i, arr));
}
return result;
}
// С обработкой ошибок
function robustMap(arr, cb, thisArg) {
if (!Array.isArray(arr)) throw new TypeError('First argument must be an array');
if (typeof cb !== 'function') throw new TypeError('Callback must be a function');
const result = [];
for (let i = 0; i < arr.length; i++) {
result[i] = cb.call(thisArg, arr[i], i, arr);
}
return result;
}
// Производительность
const arr = Array.from({ length: 1000 }, (_, i) => i);
console.time('myMap');
for (let i = 0; i < 1000; i++) {
myMap(arr, x => x * 2);
}
console.timeEnd('myMap');
Пошаговый разбор
// Массив: [1, 2, 3]
// Функция: x => x * 2
// Шаг 1: Создаём пустой результат []
// Шаг 2: i=0, callback(1, 0, [1,2,3]) = 2, result = [2]
// Шаг 3: i=1, callback(2, 1, [1,2,3]) = 4, result = [2, 4]
// Шаг 4: i=2, callback(3, 2, [1,2,3]) = 6, result = [2, 4, 6]
// Шаг 5: Возвращаем [2, 4, 6]
Использование call/apply/bind
function myMap(arr, callback, thisArg) {
const result = [];
for (let i = 0; i < arr.length; i++) {
// Все три варианта эквивалентны:
result.push(callback.call(thisArg, arr[i], i, arr));
// result.push(callback.apply(thisArg, [arr[i], i, arr]));
// const bound = callback.bind(thisArg);
// result.push(bound(arr[i], i, arr));
}
return result;
}
Различие с другими методами
- map: преобразует элементы
- filter: выбирает элементы
- reduce: агрегирует в одно значение
- forEach: выполняет побочные эффекты
Ключевые моменты
- callback.call(thisArg, ...): сохраняем контекст
- Три параметра callback: element, index, array
- Новый массив: не изменяем исходный
- Разреженные массивы: пропускаем пустые слоты
- Спецификация: следуем ECMAScript стандарту