Различия между for и foreach в JavaScript 5

#javascript #for-loop #foreach

#javascript #for-цикл #foreach

Вопрос:

Я узнал, что a for выполняет итерацию по каждому элементу массива, даже если указанный регистр не существует, в то время как a forEach пропускает несуществующие регистры.

Более того, при for записи a должен быть указан счетчик (я не говорю об for...in этом), в то время как a forEach просто использует итератор функции.

Хорошо. Не могли бы вы ответить на четыре следующих вопроса, пожалуйста?

  1. Действительно ли a forEach пропускает случаи несуществующих массивов, а a — for нет?
  2. forEach Использует ли a счетчик intern (нам просто не нужно его писать)? Или это совершенно другой механизм, чем a for ?
  3. В некоторых языках a forEach не проходит через массив в том же порядке, что и порядок массива. Как насчет JavaScript
  4. Наконец, существуют ли другие различия между ними (исключая, конечно, грамматические / синтаксические)?

Комментарии:

1. чтение документов дало бы ответы на большинство ваших вопросов. forEach для

Ответ №1:

for Ключевое слово — это языковая конструкция, это самый быстрый и самый «сырой» способ перебора вещей. Он имеет три стиля:

  1. C-стиль for : for ( initializer; condition; iterator ) { code... } — это самая гибкая и проверенная временем версия. Чтобы выполнить итерацию по списку, вы начинаете перебирать все индексы списка от 0 до длины списка. Вы также можете перейти к любому другому, третьему и т.д. элементу. В большинстве случаев этого достаточно.

  2. Javascript for-in for (var key in object) { code ... } — это хороший способ перебрать каждый ключ в объекте, например, для вывода всех значений объекта JSON.

  3. (ES2015) Javascript for-of : for (var item of collection) { code ... } — этот новый и доступен в современных браузерах. Это позволяет вам пропускать индексы и счетчики и позволяет просматривать каждый элемент коллекции. (Скажем, каждый объект в списке продуктов.) Он делает то же самое, что и For в стиле C, но проще в использовании.

Однако forEach функция специфична для Array объектов в Javascript и позволяет запускать функцию для каждого элемента в массиве. Хорошо, если у вас есть служебная функция, которая делает всю работу за вас.

Вот как использовать все вышеперечисленные виды итераций:

 // most plain kind of array with a length of 7
var myArray = [1, 2, 3, 4, 5, 6, undefined];
// a weird kind of array with no values between index 3 and 100
// its length is 102 
var mySparseArray = [1, 2, 3, 4];
mySparseArray[100] = 5;
mySparseArray[101] = 6;

// 1. C-style for
// ** can control how index is incremented
// ** needs an extra variable to iterate with
for (var i = 0; i < myArray.length; i  = 1) {
    console.log(myArray[i]);
    // logs 1, 2, 3, 4, 5, 6, undefined
}

for (var i = 0; i < mySparseArray.length; i  = 1) {
    console.log(myArray[i]); 
    // logs 1, 2, 3, 4, undefined, undefined, ... (up to index 100), 5, 6
} 

// 2. for..in
// ** keys are iterated over in non-guaranteed order
//    (you might get 2, "length", 1, 0, 3)
// ** all enumerable keys are included, that might include things other than indexes. 
for (var key in myArray) {
    console.log(myArray[key]);
    // logs 1, 2, 3, 4, 5, 6 
}

for (var key in mySparseArray) {
    console.log(mySparseArray[key]);
    // logs 1, 2, 3, 4, 5, 6
    // this for-loop "thinks" the array is an object with numbers for keys
}

// 3. for..of
// ** only available in browsers with ES2015 support
// ** supports many other things than Arrays - TypedArrays, Iterators...
for (var item of myArray) {
    console.log(item);
}

for (var item of mySparseArray) {
    console.log(item);
    // logs 1, 2, 3, 4, undefined, undefined, ... (up to index 100), 5, 6
}

// 4. forEach
// ** calls a function for each element (considered slow)
// ** supports only Arrays (unless you call it with Array.prototype.forEach.call)
function myCallback(element) {
  console.log(element);
}
myArray.forEach(myCallback); // logs 1, 2, 3, 4, 5, 6
mySparseArray.forEach(myCallback); // logs 1, 2, 3, 4, 5, 6
  

Обязательно попробуйте их в консоли разработчика вашего браузера! Вы можете увидеть каждый из приведенных выше фрагментов кода в действии.

Обязательно проверяйте ссылку MDN на итерациях.

Комментарии:

1. Что касается пропуска, OP означает конкретно не существующие свойства, а не только те, у undefined которых есть значение. И .forEach действительно пропускает их, попробуйте использовать разреженный массив.

2. length не должно быть перечислимым, поэтому оно не повторяется for...in .

Ответ №2:

Итак, когда вы говорите о for цикле, вы имеете в виду фактический for цикл, а не for...in (который не должен использоваться в массивах). Похоже, существует небольшое заблуждение, и позвольте мне начать с этого:

for Цикл не предназначен для перебора массивов. Не то, чтобы вы не должны или это плохо, но это не его основная функция — он буквально просто зацикливается до тех пор, пока не будет выполнено условие, после чего он останавливается. Наиболее распространенным синтаксисом является for (var i =0; i < max; i ) тот, который хорошо подходит для цикла массива, но это не значит, что вы не можете иметь, скажем, for (var i = 100; Number.isInteger(i); i = i/2) . Итак, с учетом сказанного, вот ответы на ваши вопросы:

1. forEach Пропускает ли отсутствующие элементы в массиве?

Ответ: Да

forEach это метод массива map , и наряду с reduce другими он пропускает неназначенные слоты. Это должно помочь в работе с разреженными массивами, например

 var arr = [];
arr[5] = "five";
arr[7] = "seven";
arr[10] = "ten";

console.log("---using forEach---");
arr.forEach(function(item) {
  console.log(item);
});

console.log("---using a for-loop---");
for (var i = 0; i < arr.length; i  ) {
  console.log(arr[i]);
}  

Это простой пример, который иллюстрирует, почему вы можете захотеть использовать цикл разреженного массива — как вы можете видеть, вы получаете намного меньше выполнения кода. Вот немного более сложный:

 var arr = [];
arr[5] = "five";
arr[42] = "the answer";
arr[9001] = "over nine thousand";

arr.forEach(function(item) {
  console.log(item);
});  

Прямой for цикл будет выполняться (буквально) более девяти тысяч раз, когда все, что у вас есть, это три элемента.

Я должен кое-что уточнить здесь — сброшенные значения — это любые значения, которым вы ничего не присвоили. Назначение undefined считается назначением по-прежнему:

 var arr = [];
arr[5] = "five";
arr[7] = undefined;
arr[10] = "ten";

arr.forEach(function(item) {
  console.log(item);
});  

2. forEach Использует ли внутренний счетчик

Ответ: да

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

 var arr = [];
arr[5] = "five";
arr[42] = "the answer";
arr[9001] = "over nine thousand";

arr.forEach(function(item, index) {
  console.log(index, "-", item);
});  

3. В каком порядке выполняется forEach

Ответ: начинается с нуля и идет в порядке возрастания

Вышеизложенное должно показать вам это. Вот еще один краткий пример:

 var alphabet = ["a", "b", "c", "d", "e", "f", "g" ];

alphabet.forEach(function(letter, index) {
  console.log(index, letter);
})  

4. Различия между for и forEach

Ответ: их сложно сравнивать

Различия между ними — это то, на что я ссылался в начале — for это общий оператор цикла в JavaScript — вы можете использовать его для просмотра массивов, но вы можете использовать его и для других целей. forEach , напротив, он специально привязан к массивам. Очень сложно реально сравнивать два, но если мы ограничимся только разговорами об их использовании в массивах, вот несколько моментов

for

  • Вы можете выйти из цикла раньше, используя break or return , поэтому вам не нужно перебирать весь массив
  • Вы можете управлять обходом массива, поэтому вы можете начать с конца и отсчитывать ( for (var i = arr.length; i >= 0; i--) ) или, возможно, даже вернуться назад или пропустить элементы (выполнение i-- или i = 2 внутри тела цикла)
  • Хорошо известен как механизм цикла, поскольку он используется во многих других языках
  • /- Вы будете выполнять цикл по всей длине массива, даже для разреженных массивов.
  • — Вы должны поддерживать счетчик циклов, даже если он вас не волнует.

forEach

  • Хорошо работает с разреженными массивами
  • Принимает обратные вызовы, поэтому вы можете повторно использовать функции, передавая их в .forEach вызов
  • Еще одним преимуществом вышесказанного является функциональная область для всего, что находится внутри тела обратного вызова (хотя с ES6 let и const это имеет меньшее значение)
  • Идиоматический JavaScript
  • /- Вы будете просматривать только существующие элементы в разреженном массиве
  • — Невозможно завершить досрочно, хотя вы могли бы использовать find .
  • — У вас нет контроля над тем, как проходит массив.

PS: Почетное упоминание относится к for...of

Благодаря @Bergi я вспомнил кое-что, что хотел включить сюда:

Один цикл, который вы не упомянули, но который существует, — это for...of цикл, который похож по синтаксису на for...in цикл, но он предназначен для работы с массивами, а также с другими итеративными объектами. Это относительно новая конструкция ( forEach по крайней мере, более новая), как указано в спецификациях ES6, поэтому широкая веб-поддержка может отсутствовать, но это альтернатива обоим из вышеперечисленных. Он довольно прост в использовании:

 var arr = [];
arr[5] = "five";
arr[7] = "seven";
arr[10] = "ten";

for (item of arr) {
  console.log(item);
}  

Короче говоря, это работает как for цикл, поскольку он перебирает все, но вам не нужно объявлять и поддерживать счетчик.

Комментарии:

1. Вопрос о том, хорошо ли forEach работает с разреженными массивами, является спорным, многие ожидают получить undefined значения вместо пропуска.

2. Я бы рассмотрел for и for…of гораздо более идиоматичный, чем .forEach

3. @Bergi о разреженных массивах, это, вероятно, будет зависеть от того, как вы хотите их обработать. По моему опыту, большую часть времени я хотел бы только то, что в них присутствует, хотя я бы сказал, что это не всегда. Я определенно вижу применение для перебора всего. Однако, похоже, что JavaScript (или, возможно, ECMAScript) занял позицию, согласно которой разреженные массивы должны состоять только из любого из его существующих членов. Итак, «хорошо», возможно, потребуется указать там, но для некоторых случаев использования это подходит.

4. @Bergi и about for , это везде идиоматично (вероятно, следовало бы сформулировать это именно так, а не «Хорошо узнаваемо»), но я бы не стал считать это специфичным для JS. Методы массива, такие как forEach , map , reduce , filter , и find , являются более отличными от JavaScript. Конечно, они существуют вне ит, но когда речь идет о механизмах обработки массивов JS, они заслуживают своего места, что делает их выделяющимися и хорошо узнаваемыми.

Ответ №3:

влаз уже ответил в комментариях, я бы просто:

Основное отличие заключается в том, что forEach вызов может генерировать одно замыкание по элементу в массиве, что очень полезно, когда вам нужно повторно использовать некоторые внутренние переменные.