Какое применение имеет метод javascript forEach (эта карта не может работать)?

91

Единственная разница, которую я вижу в map и foreach, заключается в том, что mapона возвращает массив, а forEachне возвращает . Однако я даже не понимаю последнюю строчку forEachметода " func.call(scope, this[i], i, this);". Например, не « this» и « scope» со ссылкой на тот же объект и не является , this[i]и со iссылкой на текущее значение в цикле?

Я заметил, что в другом посте кто-то сказал: «Используйте, forEachкогда вы хотите что-то сделать на основе каждого элемента списка. Например, вы можете добавлять что-то на страницу. По сути, это отлично подходит, когда вам нужны« побочные эффекты ». Я не знаю, что подразумевается под побочными эффектами.

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

Наконец, есть ли какое-либо реальное использование этих методов в javascript (поскольку мы не обновляем базу данных), кроме как манипулировать числами, подобными этому:

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

Спасибо за любой ответ.

ДжонМерлино
источник
Как найти определение функции для нативного JavaScript, как в случае с mapи forEach? Все, что я получаю от Google, - это спецификации использования и руководства.
WoodrowShigeru
См. Также язык-агностик. Есть ли разница между foreach и map?
Берги

Ответы:

95

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

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

"Побочный эффект" forEachзаключается в том, что исходный массив изменяется. «Без побочного эффекта» mapозначает, что при идиоматическом использовании исходные элементы массива не меняются; новый массив - это взаимно-однозначное сопоставление каждого элемента в исходном массиве - преобразование сопоставления является вашей предоставленной функцией.

Тот факт, что база данных не задействована, не означает, что вам не придется работать со структурами данных, что, в конце концов, является одной из сущностей программирования на любом языке. Что касается вашего последнего вопроса, ваш массив может содержать не только числа, но и объекты, строки, функции и т. Д.

Кен Редлер
источник
4
записка из будущего: это на самом деле чушь; .mapможет делать все, что .forEachумеет (включая изменение элементов), просто возвращает новый список, созданный с помощью функции итератора.
Трэвис Уэбб
6
Ответ из прошлого: Я с уважением не согласен. .map()создает новый массив и не изменяет оригинал. Вы действительно можете изменить массив, который сам отображается, но это было бы по крайней мере неидиоматично, если не бессмысленно. .forEach(), хотя и похоже, применяет свою функцию к каждому элементу, но всегда возвращает undefined. Несмотря на все вышесказанное, ОП также спрашивает о своих собственных специфических функциях, добавленных в прототип массива; раньше здесь не было ES5.
Кен Редлер,
4
По-прежнему нет ничего такого forEach, с чем вы не могли бы сделать map. Вы можете просто не заботиться о возвращаемом значении, mapи у вас будет forEach. Разница в том, что его очень неэффективно использовать mapдля задач, в которых вы не хотите создавать новый массив на основе результатов. Так что для них вы используете forEach.
тыкает
2
@poke: ..и аналогично, вы можете делать все, что вам нужно, с помощью простого старого forцикла. Я не возражаю, что есть некоторая разница в «эффективности», но я также не думаю, что кто-то спорит, что у одного есть магия, которую другой каким-то образом не может достичь. Хотя на самом деле есть одна вещь, которую mapнельзя сделать: не возвращать массив. Там это значение в том , JS оправдала ожидания от других языков, на поведение функциональных фаворитов , как map, reduce, filterи т.д. Например, вы могли бы реализовать с reduceRightиспользованием аналогичных строительных блоков, но почему бы не использовать reduceRight?
Кен Редлер
1
@ChinotoVokro, ваш пример явно изменяет исходный массив, присваивая его b.val = b.val**2 внутри возвращенного объекта . То, что мы говорим, действительно возможно, но сбивает с толку и не является идиоматическим. Обычно вы просто возвращаете новое значение, а не присваиваете его также исходному массиву:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Кен
66

Основное различие между этими двумя методами - концептуальное и стилистическое: вы используете, forEachкогда хотите что-то сделать с каждым элементом массива или с каждым элементом массива (я думаю, что выполнение «с» - это то, что цитируемый вами пост означает «побочные эффекты») , тогда как вы используете, mapкогда хотите скопировать и преобразовать каждый элемент массива (без изменения оригинала).

Поскольку оба mapи forEachвызывают функцию для каждого элемента в массиве, и эта функция определяется пользователем, вы почти ничего не можете сделать с одним, а не с другим. Возможно, хотя и некрасиво, использовать mapдля изменения массива на месте и / или делать что-то с элементами массива:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

но гораздо чище и очевиднее в отношении вашего намерения использовать forEach:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

Особенно, если, как это обычно бывает в реальном мире, elпеременная полезна для чтения человеком:

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

Таким же образом вы можете легко использовать forEachдля создания нового массива:

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

но чище использовать map:

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

Также обратите внимание, что, поскольку mapсоздается новый массив, он, вероятно, вызывает по крайней мере некоторое снижение производительности / памяти, когда все, что вам нужно, это итерация, особенно для больших массивов - см. Http://jsperf.com/map-foreach

Что касается того, почему вы хотите использовать эти функции, они полезны в любое время, когда вам нужно выполнять манипуляции с массивами в javascript, что (даже если мы просто говорим о javascript в среде браузера) довольно часто, почти в любое время вы обращаетесь к массиву, который не записываете вручную в коде. Возможно, вы имеете дело с массивом элементов DOM на странице или данными, полученными из запроса AJAX, или данными, введенными в форму пользователем. Один из распространенных примеров, с которыми я сталкиваюсь, - это извлечение данных из внешнего API, где вы, возможно, захотите использовать mapдля преобразования данных в forEachнужный формат, а затем использовать для итерации по вашему новому массиву, чтобы отобразить его вашему пользователю.

Нрабинович
источник
Я вижу, что люди .map()все время используют для изменения встроенных элементов. Я был под впечатлением , что было главным преимуществом использования .mapболее.forEach
chovy
1
@chovy Я считаю, что вы должны выбирать, исходя из того, хотите ли вы получить массив результатов. .mapможет изменять элементы, да, но это создаст массив, который вам не нужен. Вам также следует подумать о том, как выбор того или другого позволяет читателю угадать, есть ли у вас побочные эффекты,
leewz
16

Проголосованный ответ (от Кена Редлера) вводит в заблуждение.

Побочный эффект в информатике означает, что свойство функции / метода изменяет глобальное состояние [wiki] . В некотором узком смысле это может также включать чтение из глобального состояния, а не из аргументов. В императивном или объектно-ориентированном программировании побочные эффекты проявляются чаще всего. И вы, вероятно, используете это, даже не осознавая.

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

Что касается причины, по которой люди говорят, forEachчто используется, когда вам нужен побочный эффект, это то, что возвращаемое значение forEachвсегда undefined. Если у него нет побочного эффекта (не меняет глобальное состояние), то функция просто тратит время процессора. Оптимизирующий компилятор устранит этот блок кода и заменит его окончательным значением ( undefined).

Кстати, следует отметить, что JavaScript не имеет ограничений на побочные эффекты. Вы все еще можете изменить исходный массив внутри map.

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]
Джек Тан
источник
2
Этот вопрос, его ответы и комментарии всегда интересно возвращаться каждые пару лет. То, как карта реализована в JS с точки зрения распределения памяти, - еще один приятный момент.
Кен
7

Это красивый вопрос с неожиданным ответом.

Следующее основано на официальном описанииArray.prototype.map() .

Нет ничего, что forEach()можно сделать, чего map()нельзя. То есть, map()является строгим супер-набор из forEach().

Хотя map()обычно используется для создания нового массива, его также можно использовать для изменения текущего массива. Следующий пример иллюстрирует это:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

В приведенном выше примере aбыло удобно установить a[i] === iдля i < a.length. Тем не менее, он демонстрирует мощь map()и, в частности, его способность изменять массив, для которого он вызывается.

Примечание1:
официальное описание подразумевает, что map()может даже измениться длина массива, на котором он вызывается! Однако я не вижу (веских) причин для этого.

Примечание 2:
несмотря на то, что map()карта является супернаборомforEach() , forEach()ее все равно следует использовать там, где требуется изменить данный массив. Это проясняет ваши намерения.

Надеюсь, это помогло.

Сумух Барве
источник
8
На самом деле есть одна вещь, которую forEach может сделать, а карта не может - не возвращать массив.
Антти Хаапала
1
Вы также можете использовать третий аргумент функции сопоставления для изменения целевого массива вместо переменной с областью действия:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926 06
@ pdoherty926 правда, но может a.forEach(function(x, i, arr) { ..., что, опять же, концептуально более верный способ, когда вы намерены изменить каждый исходный элемент, а не отображать значения из одного массива в другой
conny
@conny: Согласен. Также см. Этот ответ на аналогичный вопрос.
Sumukh Barve
3

Вы можете использовать mapкак если бы это было forEach.

Однако он сделает больше, чем должен.

scopeможет быть произвольным объектом; это ни в коем случае не обязательно this.

Что касается того, есть ли реальное использование для mapи forEach, а также спросить, есть ли реальное использование для циклов forили while.

Wombleton
источник
Но почему здесь вызываются и "scope", и "this": func.call (scope, this [i], i, this); Разве область видимости не является параметром, равным текущему объекту, которым является «это»?
JohnMerlino
Нет, он может быть равен текущему объекту. Сам объект передается в массив как третий параметр. scope = scope || thisозначает «если scopeложно (undefined, null, false и т. д.), thisвместо этого установите область действия и продолжайте».
wombleton
Вы можете связать меня с примером, когда он не равен этому?
JohnMerlino
developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… есть один в разделе «Печать содержимого массива с помощью объектного метода»
wombleton
1

Хотя все предыдущие вопросы верны, я бы определенно провел другое различие. Использование mapи forEachможет подразумевать намерение.

Мне нравится использовать, mapкогда я просто каким-то образом преобразую существующие данные (но хочу убедиться, что исходные данные не изменились.

Мне нравится использовать, forEachкогда я модифицирую коллекцию на месте.

Например,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

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

Если у вас нет реальной необходимости изменять существующий список, нет смысла изменять его на месте, и он лучше подходит для функциональных / неизменяемых стилей программирования.

Maschwenk
источник
1

TL; DR ответ -

map всегда возвращает другой массив.

forEach - нет. Вам решать, что он делает. Верните массив, если хотите, или сделайте что-нибудь еще, если нет.

Гибкость желательна в определенных ситуациях. Если это не то, с чем вы имеете дело, используйте карту.

чарси
источник
0

И снова я чувствую себя некромантом, добавляющим ответ на вопрос, который был задан 5 лет назад (к счастью, активность была довольно недавняя, так что я не единственный, кто тревожит мертвых :).

Другие уже писали о вашем главном вопросе относительно разницы между функциями. Но для...

есть ли какое-либо реальное использование этих методов в javascript (поскольку мы не обновляем базу данных), кроме как для управления числами, подобными этому:

... забавно спросить. Буквально сегодня я написал фрагмент кода, который присваивает несколько значений из регулярного выражения нескольким переменным, используя карту для преобразования. Он использовался для преобразования очень сложной текстовой структуры в визуализируемые данные ... но для простоты я предлагаю пример с использованием строк даты, потому что они, вероятно, более знакомы всем (хотя, если бы моя проблема действительно была с датами , вместо map я бы использовал Date-объект, который бы отлично справился со своей задачей).

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

Итак ... пока это был всего лишь пример - и выполнялось только очень простое преобразование данных (просто для примера) ... выполнение этого без карты было бы гораздо более утомительной задачей.

Конечно, это написано в версии JavaScript , что я не думаю , что слишком многие браузеры поддерживают пока (по крайней мере , полностью) , но - мы получаем там. Если бы мне нужно было запустить его в браузере, я считаю, что это было бы хорошо.

Маркку Уттула
источник