Для кожного над масивом у JavaScript


4682

Як я можу переглядати всі записи в масиві за допомогою JavaScript?

Я думав, що це щось подібне:

forEach(instance in theArray)

Де theArrayмій масив, але це здається невірним.


16
Я це шукав, але шукав forEach і не просто for. як було сказано, в c # було трохи інакше, і це мене бентежило :)
Dante1986

6
ECMAScript & nbsp; 6, можливо, буде містити конструкцію "за ...". Докладнішу інформацію див. У ... з (MDN). Ви вже можете спробувати його з останніми версіями Firefox.
Slaven Rezic

36
Array.ForEach приблизно на 95% повільніше, ніж для () у кожному для масивів у JavaScript. Дивіться цей тест на ефективність в Інтернеті: jsperf.com/fast-array-foreach через coderwall.com/p/kvzbpa
molokoloco

77
У багатьох ситуаціях 95% повільніше не стане важливим blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html
Девід Сайкс

7
На відміну від цього , в Python є більш ефективним , щоб використовувати функції , ніж використання традиційних для петель. (Врахуйте , що i < lenі i++може бути зроблено з допомогою двигуна, а не перекладач.)
joeytwiddle

Відповіді:


7019

TL; DR

  • Не використовуйте, for-inякщо ви не використовуєте їх із захисними засобами або хоча б не знаєте, чому це може вас укусити.
  • Як правило, найкращі ставки

    • for-ofцикл (ES2015 + тільки),
    • Array#forEach( spec| MDN) (або її родичів someта таких) (лише для ES5 +),
    • проста старомодна forпетля,
    • або for-inз гарантіями.

Але є ще багато чого для вивчення, читання далі ...


JavaScript має потужну семантику для циклічного перегляду масивів та об’єктів, подібних до масиву. Відповідь я розділив на дві частини: Параметри справжнього масиву та параметри для речей, які просто схожі на масив, таких як argumentsоб'єкт, інші ітерабельні об'єкти (ES2015 +), колекції DOM тощо.

Я швидко відзначити , що ви можете використовувати ES2015 варіанти в даний час , навіть на двигунах ES5, по transpiling ES2015 в ES5. Шукати "ES2015 transpiling" / "ES6 transpiling" для отримання додаткової інформації ...

Гаразд, давайте розглянемо наші варіанти:

Для фактичних масивів

У ECMAScript 5 ("ES5") є три варіанти, найширша на даний момент версія, і ще два додані в ECMAScript 2015 ("ES2015", "ES6"):

  1. Використання forEachта пов'язані з ним (ES5 +)
  2. Використовуйте просту forпетлю
  3. Використовуйте for-in правильно
  4. Використовувати for-of(використовувати ітератор неявно) (ES2015 +)
  5. Використовуйте ітератор явно (ES2015 +)

Деталі:

1. Використання forEachта супутнє

У будь-якому нечітко сучасному середовищі (наприклад, не IE8), у якому ви маєте доступ до Arrayфункцій, доданих ES5 (безпосередньо або за допомогою поліфілів), ви можете використовувати forEach( spec| MDN):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEachприймає функцію зворотного дзвінка і, необов'язково, значення, яке слід використовувати, як thisпри виклику цього зворотного дзвінка (не використовується вище). Зворотний виклик викликається для кожного запису в масиві для того, щоб пропускати неіснуючі записи в розріджених масивах. Хоча я використовував лише один аргумент вище, зворотний виклик викликається трьома: значенням кожного запису, індексом цього запису та посиланням на масив, який ви повторюєте (на випадок, якщо ваша функція вже не є зручною ).

Якщо ви не підтримуєте застарілі веб-переглядачі на зразок IE8 (який NetApps показує трохи більше 4% частки ринку на момент написання цього повідомлення у вересні 2016 року), ви можете із задоволенням використовувати forEachна веб-сторінці загального призначення без пошкоджень. Якщо вам потрібно підтримувати застарілі браузери, шиммінг / поліфілінг forEachлегко зробити (пошук "es5 shim" для декількох варіантів).

forEach має перевагу, що вам не доведеться оголошувати змінні індексації та значення в області, що містить, оскільки вони подаються як аргументи функції ітерації, і так добре підходять до саме цієї ітерації.

Якщо ви турбуєтесь про витрати часу виконання виклику функції для кожного запису масиву, не будьте; деталі .

Крім того, forEachє функція "цикл через них усіх", але ES5 визначає кілька інших корисних функцій "пропрацюйте свій масив і виконайте справи", включаючи:

  • every(припиняє циклічно вперше повертатися зворотний виклик falseабо щось фальсифіковане)
  • some(припиняє циклічно вперше повертатися зворотний виклик trueчи щось тривожне)
  • filter(створює новий масив, що включає елементи, де функція фільтра повертається trueі опускає ті, де він повертається false)
  • map (створює новий масив із значень, повернутих зворотним дзвоном)
  • reduce (створює значення шляхом повторного виклику зворотного виклику, передаючи попередні значення; деталі див. у специфікації; корисно підсумовувати вміст масиву та багато іншого)
  • reduceRight(як reduce, але працює у порядку зменшення, а не у порядку зростання)

2. Використовуйте просту forпетлю

Іноді найкращі старі способи:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

Якщо довжина масиву не буде змінюватися в протягом циклу, і це у виконанні чутливої коди (малоймовірно), трохи більше складний варіант захоплення довжиною фронту може бути крихітними трохи швидше:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

І / або рахуючи відсталі:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

Але в сучасних JavaScript-двигунах рідко вам потрібно витягти цей останній шматочок соку.

У ES2015 та новіших версіях ви можете зробити свої змінні індексу та значення локальними для forциклу:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
    console.log(index, value);
}
//console.log(index);   // would cause "ReferenceError: index is not defined"
//console.log(value);   // would cause "ReferenceError: value is not defined"

І коли ви це робите, не просто, valueа й indexвідтворюється для кожної ітерації циклу, тобто закриття, створені в тілі циклу, зберігають посилання на indexvalue), створені для цієї конкретної ітерації:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        console.log("Index is: " + index);
    });
}

Якщо у вас було п'ять дівок, ви отримаєте "Індекс - 0", якщо ви натиснули перший, а "Індекс - 4", якщо ви натиснули останній. Це не працює, якщо ви використовуєте varзамість let.

3. Користуйтеся for-in правильно

Ви будете отримувати люди , які говорять вам використовувати for-in, але це не те , що for-inдля . for-inпроходить через перелічені властивості об'єкта , а не індекси масиву. Замовлення не гарантується , навіть у ES2015 (ES6). ES2015 + визначає порядок об’єкта властивостей (через [[OwnPropertyKeys]], [[Enumerate]]та речі, що їх використовують як Object.getOwnPropertyKeys), але він не визначив, що for-inбуде слідувати цьому порядку; ES2020, хоча. (Деталі в цій іншій відповіді .)

Єдині реальні випадки використання для for-inмасиву:

  • Це розріджений масив з масивними прогалинами в ньому, або
  • Ви використовуєте неелементні властивості і хочете включити їх у цикл

Дивлячись лише на цей перший приклад: Ви можете використовувати for-inдля відвідування цих розріджених елементів масиву, якщо використовуєте відповідні гарантії:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These checks are
        /^0$|^[1-9]\d*$/.test(key) &&    // explained
        key <= 4294967294                // below
        ) {
        console.log(a[key]);
    }
}

Зверніть увагу на три чеки:

  1. Те , що об'єкт має свою власну власність під цим ім'ям (не один він успадковує від свого прототипу), і

  2. Ключ - усі десяткові цифри (наприклад, звичайна форма рядка, а не наукове позначення) та

  3. Це значення ключа при примусовому до числа є <= 2 ^ 32 - 2 (що становить 4 294 967 294). Звідки це число? Це частина визначення індексу масиву в специфікації . Інші числа (нецілі числа, від’ємні числа, числа, що перевищують 2 ^ 32 - 2) не є індексами масиву. Причина, що це 2 ^ 32 - 2, полягає в тому, що найбільше значення індексу стає нижчим за 2 ^ 32 - 1 , що є максимальним значенням, яке lengthможе мати масив . (Наприклад, довжина припадки у вигляді масиву в 32-розрядний беззнаковое ціле число.) (Належить до RobG за вказівку в коментарі на моєму блозі , що мій попередній тест був не зовсім правий.)

Ви б, звичайно, не робили цього вбудованого коду. Ви б написали функцію утиліти. Можливо:

4. Використовувати for-of(використовувати ітератор неявно) (ES2015 +)

ES2015 додав ітератори до JavaScript. Найпростіший спосіб використання ітераторів - це новий for-ofоператор. Це виглядає приблизно так:

const a = ["a", "b", "c"];
for (const val of a) {
    console.log(val);
}

Під обкладинками, що отримує ітератор з масиву і проходить через нього, отримуючи з нього значення. У цьому не виникає проблеми, яку for-inмає, оскільки він використовує ітератор, визначений об'єктом (масивом), а масиви визначають, що їх ітератори повторюють через записи (а не їх властивості). На відміну від for-inES5, порядок відвідування записів - це числовий порядок їх індексів.

5. Використовуйте ітератор явно (ES2015 +)

Іноді, можливо, ви хочете явно використовувати ітератор . Ви можете зробити це теж, хоча це набагато незграбніше, ніж . Це виглядає приблизно так:for-of

const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

Ітератор - це об'єкт, що відповідає визначенню Ітератора в специфікації. Його nextметод повертає новий об'єкт результату щоразу, коли ви його викликаєте. Об'єкт результату має властивість, яка doneговорить нам, чи це зроблено, і властивість, що valueмає значення для цієї ітерації. ( doneнеобов'язково, якби це було false, valueнеобов’язково, якби було undefined.)

Значення valueзмінюється залежно від ітератора; масиви підтримують (принаймні) три функції, які повертають ітератори:

  • values(): Це я використовував вище. Вона повертає ітератор , де кожен valueє елементом масиву для цієї ітерації ( "a", "b"і "c"в прикладі вище).
  • keys(): Повертає ітератор, де кожен valueє ключем для цієї ітерації (так, як для нашого aвище, це було б "0", тоді "1", тоді "2").
  • entries(): Повертає ітератор, де кожен valueє масивом у формі [key, value]для цієї ітерації.

Для об’єктів, подібних до масиву

Крім справжніх масивів, є також об’єкти, схожі на масив, які мають lengthвластивість та властивості з числовими іменами: NodeListекземпляри, argumentsоб’єкт тощо. Як ми перебираємо їх вміст?

Використовуйте будь-який із варіантів, описаних вище для масивів

Принаймні деякі, а можливо, більшість або навіть усі підходи до масиву, наведені вище, часто однаково добре застосовуються до об’єктів, подібних до масиву:

  1. Використання forEachта пов'язані з ним (ES5 +)

    Різні функції на Array.prototype"навмисно загальних" і, як правило, можуть використовуватися на об'єктах, подібних до масиву, через Function#callабо Function#apply. (Дивіться Caveat щодо наданих господарями об’єктів наприкінці цієї відповіді, але це рідкісна проблема.)

    Припустимо , ви хочете використовувати forEachна безлічі А Node«S childNodesвласності. Ви зробите це:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });

    Якщо ви будете робити це багато, ви можете захопити копію посилання на функцію в змінну для повторного використання, наприклад:

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
  2. Використовуйте просту forпетлю

    Очевидно, простий forцикл стосується об’єктів, подібних до масиву.

  3. Використовуйте for-in правильно

    for-inз тими ж захисними засобами, що і з масивом, також слід працювати з об’єктами, схожими на масив; може застосовуватися застереження для об’єктів, наданих господарем на №1 вище.

  4. Використовувати for-of(використовувати ітератор неявно) (ES2015 +)

    for-ofвикористовує ітератор, наданий об'єктом (якщо такий є). Це включає об'єкти, надані хостом. Наприклад, специфікацію для NodeListвід querySelectorAllоновлено для підтримки ітерації. Спеціалізації для з HTMLCollectionбоку getElementsByTagNameне було.

  5. Використовуйте ітератор явно (ES2015 +)

    Див. №4.

Створіть справжній масив

В іншому випадку ви можете перетворити об’єкт, схожий на масив, у справжній масив. Робити це напрочуд просто:

  1. Використовуйте sliceметод масивів

    Ми можемо використовувати sliceметод масивів, який, як і інші методи, згадані вище, є "навмисно загальним", і тому може використовуватися з об'єктами, схожими на масив, наприклад:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);

    Так, наприклад, якщо ми хочемо перетворити NodeListв справжній масив, ми могли б зробити це:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));

    Дивіться Caveat для наданих господарями об’єктів нижче. Зокрема, зауважте, що це не вдасться в IE8 і раніше, що не дозволяє використовувати надані хостом об’єкти як thisподібні.

  2. Використовувати синтаксис спред ( ...)

    Також можливо використовувати синтаксис поширення ES2015 з двигунами JavaScript, які підтримують цю функцію. Наприклад for-of, для цього використовується ітератор, наданий об'єктом (див. №4 у попередньому розділі):

    var trueArray = [...iterableObject];

    Так, наприклад, якщо ми хочемо перетворити a NodeListв справжній масив, з синтаксисом розповсюдження це стає досить лаконічним:

    var divs = [...document.querySelectorAll("div")];
  3. Використовуйте Array.from

    Array.from (специфікація) | (MDN) (ES2015 +, але легко поліфільований) створює масив з об’єкта, подібного до масиву, необов'язково спочатку передаючи записи через функцію зіставлення. Тому:

    var divs = Array.from(document.querySelectorAll("div"));

    Або якщо ви хочете отримати масив імен тегів елементів із заданим класом, ви використовуєте функцію зіставлення:

    // Arrow function (ES2015):
    var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Standard function (since `Array.from` can be shimmed):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });

Застереження для наданих господарем об'єктів

Якщо ви використовуєте Array.prototypeфункції з хоста , що надається масив типу об'єктів (списки DOM і інших речей , передбачених в браузері , а не двигун JavaScript), ви повинні бути впевнені, що тест в вашої цільової середовищі , щоб переконатися , що приймає наданий об'єкт поводиться правильно . Більшість поводиться належним чином (зараз), але важливо перевірити. Причина полягає в тому, що більшість Array.prototypeметодів, які ви, ймовірно, захочете використовувати, покладаються на наданий хостом об’єкт, що дає чесну відповідь на абстрактну [[HasProperty]]операцію. Станом на це написання, браузери роблять дуже гарну роботу з цього, але специфікація 5.1 дозволяє зробити можливість, що надається хостом об'єктом може бути не чесним. Це в §8.6.2 , кілька абзаців під великою таблицею на початку цього розділу), де написано:

Приймаючі об'єкти можуть реалізувати ці внутрішні методи будь-яким чином, якщо не вказано інше; Наприклад, одна з можливостей полягає в тому, що [[Get]]і [[Put]]для певного хост-об'єкта дійсно вибирають і зберігають значення властивостей, але [[HasProperty]]завжди генерують помилкові .

(Я не міг знайти еквівалентну багатослівність у специфікації ES2015, але це все одно має бути так.) Знову ж таки, з цього написання звичайні об’єкти, подібні до масиву, подібні до масивів у сучасних браузерах [ NodeListнаприклад, наприклад] , обробляють [[HasProperty]]правильно, але важливо перевірити.)


44
Я також хотів би додати, що .forEachце неможливо ефективно розбити. Ви повинні кинути виняток, щоб виконати перерву.
Піджун

82
@Pius: Якщо ви хочете зламати цикл, можете скористатися some. (Я б вважав за краще дозволити також зламати forEach, але вони, гм, не питали мене; ;-))
TJ Crowder

6
@TJCrowder Правда, хоча це більше схоже на обхід, оскільки це не є його основною метою.
Піжусень

8
@ user889030: Вам потрібно ,після k=0, а не ;. Пам’ятайте, програмування - це багато речей, серед яких пильна увага до деталей ... :-)
TJ Crowder

5
@JimB: Це описано вище (і lengthце не метод). :-)
TJ Crowder

512

Примітка . Ця відповідь безнадійно застаріла. Для більш сучасного підходу подивіться на методи, доступні в масиві . Методами, що цікавлять, можуть бути:

  • для кожного
  • карта
  • фільтр
  • блискавка
  • зменшити
  • кожен
  • дещо

Стандартний спосіб ітерації масиву в JavaScript - це ванільна for-loop:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element
}

Однак зауважте, що такий підхід хороший лише у тому випадку, якщо у вас є щільний масив, і кожен індекс зайнятий елементом. Якщо масив є рідким, тоді ви можете зіткнутися з проблемами продуктивності при такому підході, оскільки ви повторите багато індексів, які насправді не існують у масиві. У цьому випадку for .. inкраща ідея може бути -loop. Однак ви повинні використовувати відповідні гарантії, щоб переконатися, що діють лише бажані властивості масиву (тобто елементи масиву), оскільки for..in-loop також буде перерахований у застарілі браузери або якщо додаткові властивості визначені як enumerable.

У ECMAScript 5 для прототипу масиву буде метод forEach, але він не підтримується у застарілих браузерах. Отже, щоб мати можливість послідовно використовувати його, ви повинні мати середовище, яке його підтримує (наприклад, Node.js для JavaScript на сервері), або використовувати "Polyfill". Однак поліфіл для цієї функціональності є тривіальним, і оскільки він полегшує читання коду, його добре включити.


2
чому це for(instance in objArray) не правильне використання? мені це здається більш простим, але я чую, як ти говориш про це як не про правильний спосіб використання?
Dante1986

21
Ви можете використовувати кешування вбудованої довжини: для (var i = 0, l = arr.length; i <l; i ++)
Роберт Гофман

3
Чи є кома в кінці першого рядка навмисною, чи це друкарська помилка (може бути крапкою з комою)?
Мохд Абдул Муджіб

6
@ gardeha-Web Це навмисно. Це дозволяє нам оголосити кілька змінних одним- varключем. Якби ми використовували крапку з комою, то elementце було б оголошено в глобальному масштабі (або, вірніше, JSHint кричав би на нас, перш ніж він досяг виробництва).
PatrikAkerstrand

239

Якщо ви використовуєте бібліотеку jQuery , ви можете використовувати jQuery.each :

$.each(yourArray, function(index, value) {
  // do your stuff here
});

Редагувати:

Відповідно до запитання, користувач хоче код у JavaScript, а не jquery, так що редагування є

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}

2
Я, мабуть, найчастіше буду використовувати цю відповідь. Це не найкраща відповідь на питання, але на практиці це буде найпростішим і найбільш застосовним для тих, хто використовує jQuery. Я думаю, ми повинні всі вивчити спосіб ванілі, хоча. Ніколи не зашкодить розширити своє розуміння.
мопсид

47
Тільки заради цього: jQuery кожен набагато повільніше, ніж рідні рішення. JQuery радить використовувати нативний JavaScript замість jQuery, коли це можливо. jsperf.com/browser-diet-jquery-each-vs-for-loop
Кевін Босс

8
Утримайтеся від використання jQuery, коли ви можете використовувати vanilla js
Noe

2
Дотримуйтесь стандартного JS, не
допускайте

116

Петля назад

Я думаю, що реверс для циклу заслуговує на згадку тут:

for (var i = array.length; i--; ) {
     // process array[i]
}

Переваги:

  • Вам не потрібно оголошувати тимчасову lenзмінну або порівнювати з array.lengthкожною ітерацією, яка може бути хвилинною оптимізацією.
  • Видалення братів і сестер з ДОМ у зворотному порядку зазвичай ефективніше . (Браузер повинен робити менше переміщення елементів у своїх внутрішніх масивах.)
  • Якщо ви модифікуєте масив під час циклу, на або після індексу i (наприклад, ви видаляєте або вставляєте елемент у array[i]), то цикл прямого переходу пропустить елемент, який змістився вліво в положення i , або повторно обробить i- й елемент, який був зміщений праворуч. У традиційному циклі ви можете оновити i вказати на наступний елемент, який потребує обробки - 1, але просто повернення напрямку ітерації часто є більш простим і елегантним рішенням .
  • Аналогічно, при зміні або видаленні вкладених елементів DOM обробка в зворотному порядку може обійти помилки . Наприклад, розгляньте можливість зміни внутрішньогоHTML батьківського вузла перед тим, як обробляти його дітей. До досягнення дочірнього вузла він буде від'єднаний від DOM, замінивши новостворену дитину, коли був написаний внутрішнійHTML батьків.
  • Це коротше набрати та прочитати , ніж деякі інші доступні опції. Незважаючи на те, що він програє forEach()до ES6 for ... of.

Недоліки:

  • Він обробляє елементи в зворотному порядку. Якщо ви будували новий масив з результатів або друкуєте речі на екрані, то, природно, результат буде повернутий до початкового порядку.
  • Неодноразово вставляти братів і сестер у DOM як першу дитину, щоб зберегти їх порядок, є менш ефективним . (Веб-переглядачу не потрібно буде змінювати речі правильно.) Щоб створити вузли DOM ефективно та в порядку, просто петлю вперед і додайте як звичайно (а також використовуйте "фрагмент документа").
  • Зворотний цикл плутає молодших розробників. (Ви можете вважати це перевагою, залежно від вашого світогляду.)

Чи повинен я завжди ним користуватися?

Деякі розробники використовують зворотний зв'язок для циклу за замовчуванням , якщо немає вагомих причин для циклу вперед.

Хоча підвищення продуктивності, як правило, незначне, воно наче кричить:

"Просто робіть це до кожного пункту в списку, я не дбаю про замовлення!"

Однак на практиці , що це НЕ на насправді є надійним показником наміри, оскільки вона нічим НЕ відрізняється від тих випадків , коли ви робите піклуватися про порядок, і дійсно необхідно , щоб петлі в зворотному. Так адже ще одна конструкція буде необхідно , щоб точно висловити «не хвилює» намір, що - то в даний час недоступний в більшості мов, в тому числі ECMAScript, але які можна було б назвати, наприклад, forEachUnordered().

Якщо порядок не має значення, а ефективність викликає занепокоєння (у найпотаємнішому циклі ігрового чи анімаційного двигуна), то може бути прийнятним використовувати реверс для циклу як шаблон переходу. Просто пам’ятайте, що бачити реверс для циклу в існуючому коді не обов'язково означає, що порядок не має значення!

Краще було використовувати дляEach ()

Взагалі для коду вищого рівня, коли чіткість та безпека викликають більшу стурбованість, я раніше рекомендував використовувати Array::forEachяк шаблон за замовчуванням для циклічного циклу (хоча ці дні я вважаю за краще використовувати for..of). Причини віддавати перевагу forEachнад зворотним циклом:

  • Ясніше читати.
  • Це вказує на те, що я не збирається зміщуватися всередині блоку (що завжди є можливим сюрпризом, ховаючись у довгі forта whileпетлі).
  • Це дає вам вільний простір для закриття.
  • Це зменшує витік локальних змінних та випадкове зіткнення із (та мутацією) зовнішніх змінних.

Тоді, коли ви побачите у своєму коді зворотний цикл для циклу, це натяк на те, що він перетворений з поважної причини (можливо, одна з причин, описаних вище). І бачити традиційне перемотування вперед для циклу може вказувати на те, що може відбуватися зміщення.

(Якщо обговорення наміру не має для вас сенсу, то ви та ваш код можете отримати користь, переглянувши лекцію Крокфорда на тему « Стиль програмування та ваш мозок» .)

Зараз це ще краще використовувати для..of!

Існує дискусія щодо того, for..ofчи forEach()бажано:

  • Для максимальної підтримки браузера for..of потрібна поліфайл для ітераторів, що робить ваш додаток трохи повільнішим для виконання та трохи більшим для завантаження.

  • З цієї причини (а також для заохочення до використання mapта filter) деякі посібники зі стильного стилю забороняються for..ofповністю!

  • Але вищезазначені проблеми не стосуються програм Node.js, де for..ofзараз добре підтримується.

  • І до того await ж всередині не працюєforEach() . Використання for..of- це найяскравіший візерунок у цьому випадку.

Особисто я схильний використовувати те, що виглядає найпростіше для читання, якщо тільки виконання чи мінімізація не стали головною проблемою. Тому в ці дні я вважаю за краще використовувати for..ofзамість forEach(), але я завжди буду використовувати mapабо filterчи findабо someколи це може бути застосовано. (Ради моїх колег я рідко використовую reduce.)


Як це працює?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

Ви помітите, що i--це середній пункт (де ми зазвичай бачимо порівняння), а останній пункт порожній (там, де ми зазвичай бачимо i++). Це означає, що i--також використовується як умова продовження. Принципово важливо, що він виконується і перевіряється перед кожною ітерацією.

  • Як це можна починати, array.lengthне вибухаючи?

    Оскільки i--працює перед кожною ітерацією, під час першої ітерації ми фактично отримаємо доступ до елемента, у array.length - 1якому уникнути будь-яких проблем із елементами Array-out-of-limit undefined .

  • Чому він не зупиняє ітерацію перед індексом 0?

    Цикл припинить ітерацію, коли умова i--оцінюється до значення фальси (коли вона дорівнює 0).

    Хитрість полягає в тому, що на відміну --iвід кінцевого i--оператора, що скорочується, iале дає значення перед декрементом. Ваша консоль може продемонструвати це:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Отже, на кінцевій ітерації я раніше був 1, а i--вираз змінює його на 0, але насправді дає 1 (truthy), і тому умова проходить. На наступній ітерації i--змінюється i на -1, але виходить 0 (фальси), в результаті чого виконання негайно випадає з нижньої частини циклу.

    У традиційних вперед для циклу, i++і ++iє взаємозамінними (як Дуглас Крокфорд вказує). Однак у зворотному для циклу, оскільки наш декремент є також виразом умови, ми повинні дотримуватися, i--якщо ми хочемо обробити елемент в індексі 0.


Дрібниці

Дехто любить намалювати маленьку стрілку у зворотному forциклі та закінчити підморгуванням:

for (var i = array.length; i --> 0 ;) {

Кредити йдуть на WYL за те, що вони показали мені переваги та жахи реверсу для циклу.


3
Я забув додати орієнтири . Я також забув згадати, як зворотна циклічна робота - це значна оптимізація для 8-бітних процесорів, таких як 6502, де ви дійсно отримуєте порівняння безкоштовно!
joeytwiddle

Ця ж відповідь дається набагато більш стисло тут (на інше питання).
joeytwiddle

84

Деякі мови С- стилю використовують foreachдля перегляду перелічень. У JavaScript це робиться зі for..inструктурою циклу :

var index,
    value;
for (index in obj) {
    value = obj[index];
}

Є улов. for..inбуде проходити через кожний з перелічених членів об'єкта та членів на його прототипі. Щоб уникнути зчитування значень, успадкованих за прототипом об'єкта, просто перевірте, чи властивість належить об'єкту:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Крім того, ECMAScript 5 додав forEachметод, за допомогою Array.prototypeякого можна перерахувати масив за допомогою зворотного зв'язку (поліфайл є в документах, щоб ви все ще могли використовувати його для старих браузерів):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

Важливо зауважити, що Array.prototype.forEachвона не порушується, коли повернення дзвінка повертається false. jQuery та Underscore.js надають власні варіанти щодо eachнадання циклів, які можуть бути короткозамкненими.


3
Отже, як можна вирватися з циклу foreach ECMAScript5, як ми для нормальної для циклу або циклу foreach, як у мовах стилю С?
Ciaran Gallagher

5
@CiaranG, в JavaScript звичайно бачити eachметоди, які дозволяють return falseвикористовувати для виходу з циклу, але forEachце не варіант. Зовнішній прапор міг би бути використаний (тобто if (flag) return;, але це лише перешкоджатиме виконанню решти функціональних тіл, він forEachби продовжував ітерацію по всій колекції.
zzzzBov

43

Якщо ви хочете перевести цикл на масив, використовуйте стандартний трискладовий forцикл.

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

Ви можете отримати деякі оптимізації продуктивності, кешуючи myArray.lengthабо повторюючи їх назад.


6
для (var i = 0, length = myArray.length; i <length; i ++) слід це зробити
Edson Medina

5
@EdsonMedina Це також створить нову глобальну змінну під назвою length. ;)
joeytwiddle

4
@joeytwiddle Так, але це виходить за рамки цієї публікації. Ви все одно створите глобальну змінну i
Едсон Медіна

6
@EdsonMedina Мої вибачення, у мене це було зовсім неправильно. Використання ,після виконання завдання не вводить нового глобального, тому ваша пропозиція просто чудова ! Я плутав це через іншу проблему: Використання =після виконання завдання створює новий глобальний.
joeytwiddle

Остерігайтеся, щоб змінна i не була локальною для циклу. У JavaScript немає області блоку. Напевно, краще заявити var i, length, arrayItem;перед циклом, щоб уникнути цього непорозуміння.
Джеймс

35

Я знаю, що це старий пост, і вже так багато чудових відповідей. Для трохи більшої повноти я зрозумів, що я кину ще одну за допомогою AngularJS . Звичайно, це стосується лише тих випадків, коли ви використовуєте Angular, очевидно, проте я б хотів це все-таки поставити.

angular.forEachбере 2 аргументи та необов'язковий третій аргумент. Перший аргумент - це об'єкт (масив) для повторення, другий аргумент - це функція ітератора, а необов'язковий третій аргумент - це об'єктний контекст (в основному його називають всередині циклу як "цей".

Існують різні способи використання петлі forEach кутового. Найпростіший і, мабуть, найбільше використовується

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Ще один спосіб, корисний для копіювання елементів з одного масиву в інший, - це

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

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

angular.forEach(temp, function(item) {
    temp2.push(item);
});

Зараз є плюси і мінуси використання angular.forEachфункції на відміну від вбудованої в смак ванілі for.

Плюси

  • Легка читабельність
  • Легка писемність
  • Якщо є, angular.forEachбуде використовувати ES5 forEach. Тепер я доберуся до ефективності в розділі "мінуси", оскільки петлі forEach набагато повільніше, ніж для циклів. Я згадую це як професіонала, тому що приємно бути послідовним та стандартизованим.

Розглянемо наступні 2 вкладені петлі, які роблять точно те саме. Скажімо, у нас є 2 масиви об’єктів, і кожен об'єкт містить масив результатів, кожен з яких має властивість Value, що є рядком (або будь-яким іншим). Скажімо, нам потрібно повторити кожен результат, і якщо вони рівні, то виконайте певну дію:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

Зрозуміло, це дуже простий гіпотетичний приклад, але я написав потрійну вбудовану петлю за допомогою другого підходу, і це було дуже важко читати і писати з цього приводу.

Мінуси

  • Ефективність. angular.forEach, і рідні forEach, з цього питання, обидва набагато повільніше, ніж нормальний forцикл .... про 90% повільніше . Тому для великих наборів даних найкраще дотримуватися нативного forциклу.
  • Немає перерви, продовження чи повернення підтримки. continueнасправді підтримується " випадковістю ", щоб продовжувати в angular.forEachпростому ставити return;заяву у функції, якangular.forEach(array, function(item) { if (someConditionIsTrue) return; }); це призведе до того, що він продовжує виходити з функції для цієї ітерації. Це також пов’язано з тим, що рідний forEachне підтримує перерви або продовження.

Я впевнений, що існують і інші плюси і мінуси, і будь ласка, додайте будь-які, які вважаєте за потрібне. Я відчуваю, що, підсумок, якщо вам потрібна ефективність, дотримуйтесь лише нативної forпетлі для ваших петельних потреб. Але, якщо ваших наборів даних менше і якась ефективність, добре відмовитись в обмін на читабельність і записуваність, тоді якимось чином киньте angular.forEachцього поганого хлопця.


34

Якщо ви не проти спорожнити масив:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xбуде містити останнє значення, yі воно буде видалено з масиву. Ви також можете скористатись тим, shift()що дасть та видалить перший елемент із y.


4
Це не працює, якщо у вас є такий розріджений масив, як [1, 2, undefined, 3].
M. Grzywaczewski

2
... чи справді що-небудь фальсифікація: [1, 2, 0, 3]або[true, true, false, true]
joeytwiddle

31

Foreach реалізації ( див в jsFiddle ):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);

2
Ітератор в цьому випадку робить непотрібний розрахунок довжини. В ідеальному випадку довжину списку слід обчислювати лише один раз.
Мідхун Крішна

2
@MIdhunKrishna Я оновив свою відповідь та jsFiddle, але майте на увазі, що це не так просто, як ви думаєте. Перевірте це запитання
nmoliveira

2
Повну та правильну реалізацію можна знайти тут: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
marciowb

29

Напевно, for(i = 0; i < array.length; i++)петля - не найкращий вибір. Чому? Якщо у вас є це:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

Метод буде викликати з array[0]до array[2]. По-перше, це спочатку посилання на змінні, яких у вас навіть немає, по-друге, у вас не буде змінних у масиві, а по-третє, це зробить код сміливішим. Подивіться тут, це те, що я використовую:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

І якщо ви хочете, щоб це була функція, ви можете зробити це:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

Якщо ви хочете зламати, ще трохи логіки:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

Приклад:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Він повертає:

//Hello
//World
//!!!

29

Є три реалізації foreachв JQuery наступним чином .

var a = [3,2];

$(a).each(function(){console.log(this.valueOf())}); //Method 1
$.each(a, function(){console.log(this.valueOf())}); //Method 2
$.each($(a), function(){console.log(this.valueOf())}); //Method 3

2
Журнал консолі призначений лише для демонстрації. Отож, щоб зробити це дивним прикладом роботи.
Раджеш Пол

29

Станом на ECMAScript 6:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

Там, де можна ofуникнути диваків, пов’язаних із цим, inі змусити його працювати, як forпетля будь-якої іншої мови, і letзв’язуєi всередині циклу, на відміну від функції.

Дужки ( {}) можна пропустити, якщо є лише одна команда (наприклад, у прикладі вище).


28

Простим рішенням тепер буде використання бібліотеки underscore.js . Він надає багато корисних інструментів, таких як eachта автоматично делегує завдання рідномуforEach якщо є.

Приклад CodePen як це працює:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Дивись також


23

У for eachрідному JavaScript немає циклу . Ви можете використовувати бібліотеки, щоб отримати цю функціональність (я рекомендую Underscore.js ), використовуйте простий forу циклі.

for (var instance in objects) {
   ...
}

Однак зауважте, що можуть бути причини використовувати ще простіший forцикл (див. Запитання щодо переповнення стека Чому використання ітерації масиву настільки погана ідея? )

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}

22

Це ітератор для розширеного списку NON, де індекс починається з 0, що є типовим сценарієм роботи з document.getElementsByTagName або document.querySelectorAll)

function each( fn, data ) {

    if(typeof fn == 'string')
        eval('fn = function(data, i){' + fn + '}');

    for(var i=0, L=this.length; i < L; i++) 
        fn.call( this[i], data, i );   

    return this;
}

Array.prototype.each = each;  

Приклади використання:

Приклад №1

var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]

Приклад №2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

Кожен p тег отримує class="blue"

Приклад №3

each.call(document.getElementsByTagName('p'), 
    "if( i % 2 == 0) this.className = data;",
    'red'
);

Кожен інший тег p отримує class="red" >

Приклад №4

each.call(document.querySelectorAll('p.blue'), 
    function(newClass, i) {
        if( i < 20 )
            this.className = newClass;
    }, 'green'
);

І нарешті перші 20 синіх p тегів змінюються на зелені

Обережно при використанні рядка як функції: функція створена поза контекстом і повинна використовуватися лише там, де ви впевнені в масштабі змінних. В іншому випадку краще передавати функції там, де масштабування більш інтуїтивно зрозуміло.


21

Існує кілька способів провести цикл через масив у JavaScript, як показано нижче:

для - це найпоширеніший . Повний блок коду для циклу

var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
    text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

while - цикл, коли стан закінчується. Здається, це найшвидший цикл

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do / while - також прокручуйте блок коду, поки умова справжня, запускається принаймні один раз

var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

Функціональні цикли - forEach, map, filter, і reduce(вони перебрати функції, але вони використовуються , якщо вам потрібно що - то робити з вашим масивом і т.д.

// For example, in this case we loop through the number and double them up using the map function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

Для отримання додаткової інформації та прикладів функціонального програмування на масивах, дивіться у публікації блогу Функціональне програмування в JavaScript: карта, фільтр та зменшення .


Невелика корекція: forEachце не "функціональний" цикл, оскільки він не повертає новий Array(насправді нічого не повертає), він просто повторює.
nicholaswmin

19

ECMAScript 5 (версія на JavaScript) для роботи з масивами:

forEach - Ітераціює кожен елемент у масиві та робить все необхідне з кожним елементом.

['C', 'D', 'E'].forEach(function(element, index) {
  console.log(element + " is #" + (index+1) + " in the musical scale");
});

// Output
// C is the #1 in musical scale
// D is the #2 in musical scale
// E is the #3 in musical scale

На випадок, більше зацікавлені в роботі над масивом з використанням якоїсь вбудованої функції.

map - Він створює новий масив з результатом функції зворотного виклику. Цей метод добре використовувати, коли вам потрібно відформатувати елементи масиву.

// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
  return elem.toUpperCase();
});

// Output: ['BOB', 'JOE', 'JEN']

зменшити - Як говорить назва, він зменшує масив до єдиного значення, викликаючи задану функцію, що передає поточний елемент, і результат попереднього виконання.

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

every - Повертає true або false, якщо всі елементи масиву проходять тест у функції зворотного виклику.

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

filter - Дуже подібний до кожного, за винятком того, що фільтр повертає масив з елементами, які повертають true для заданої функції.

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

16

Немає вбудованої здатності прорватися forEach. Щоб перервати виконання, використовуйте наступне Array#some:

[1,2,3].some(function(number) {
    return number === 1;
});

Це працює, тому що someповертає true, як тільки будь-який зворотний виклик, виконаний у порядку масиву, повертає true, коротко замикаючи виконання решти. Оригінальна відповідь для деяких див. Прототип масиву


13

Я також хотів би додати це як композицію зворотного циклу та відповідь вище для того, хто також сподобається цьому синтаксису.

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

Плюси:

Перевага для цього: Ви маєте посилання вже в першому подібному, що не потрібно буде оголошувати пізніше іншим рядком. Це зручно при переході до масиву об'єктів.

Мінуси:

Це порушиться, коли посилання буде помилковим - фальси (невизначені тощо). Однак це може бути використане як перевагу. Однак читати це буде трохи важче. А також залежно від браузера він може бути "не" оптимізований для роботи швидше, ніж оригінальний.


12

jQuery спосіб, використовуючи $.map:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];

1
Я думаю, що вихід швидше буде [undefined, 2, undefined, 4, undefined, 6, undefined].

10

Використання циклів з руйнуванням ECMAScript 6 та оператором розповсюдження

Руйнування та використання оператора розповсюдження виявилися досить корисними для новачків із ECMAScript 6 як більш читабельних / естетичніших, хоча деякі ветерани JavaScript можуть вважати це безладним. Молодші чи інші люди можуть вважати це корисним.

У наступних прикладах буде використано for...ofоператор та .forEachметод.

Приклади 6, 7 і 8 можуть бути використані з якими - або функціональними петлями , такими як .map, .filter, .reduce, .sort, .every, .some. Щоб отримати додаткові відомості про ці методи, перегляньте об’єкт масиву .

Приклад 1: Нормальний for...ofцикл - тут немає хитрощів.

let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

Приклад 2: Розділіть слова на символи

let arrFruits = ['apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);
}

Приклад 3: Цикл за допомогою a keyіvalue

// let arrSimple = ['a', 'b', 'c'];

// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`
// this example will use a multi-dimensional array of the following format type:
// `arrWithIndex: [number, string][]`

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Same thing can be achieved using `.map` method
// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);

// Same thing can be achieved using `Object.entries`
// NOTE: `Object.entries` method doesn't work on Internet Explorer  unless it's polyfilled
// let arrWithIndex = Object.entries(arrSimple);

for (let [key, value] of arrWithIndex) {
  console.log(key, value);
}

Приклад 4: Отримати вбудовані властивості об'єкта

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

Приклад 5: Отримайте глибокі властивості об'єкта того, що вам потрібно

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

Приклад 6: Чи застосовується Приклад 3.forEach

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Not to be confused here, `forEachIndex` is the real index
// `mappedIndex` was created by "another user", so you can't really trust it

arrWithIndex.forEach(([mappedIndex, item], forEachIndex) => {
  console.log(forEachIndex, mappedIndex, item);
});

Приклад 7: Чи застосовується Приклад 4.forEach

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];
// NOTE: Destructuring objects while using shorthand functions
// are required to be surrounded by parentheses
arrWithObjects.forEach( ({ name, age: aliasForAge }) => {
  console.log(name, aliasForAge)
});

Приклад 8: Чи застосовується Приклад 5.forEach

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});


7

Найбільш близьким до вашої ідеї є використання, Array.forEach()яке приймає функцію закриття, яка буде виконуватися для кожного елемента масиву.

myArray.forEach(
  (item) => {
    // Do something
    console.log(item);
  }
);

Іншим життєздатним способом було б використання того, Array.map()що працює аналогічно, але воно також приймає всі значення, які ви повертаєте, і повертає їх у новому масиві (по суті, відображаючи кожен елемент на новий), як це:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]

1
Це неправильно, mapне мутує елементи масиву, оскільки він повертає новий масив, при цьому елементи цього нового є результатом застосування функції до елементів старого масиву.
Хесус Франко

Я ніколи не казав, що він не повертає новий масив, я мав на увазі зміну, застосовану функцією. Але ось я його зміню у відповіді.
Анте Яблан Адамович

знову невірно, карта не змінює і не мутує всіх елементів у вихідному масиві, я запропонував і відредагував відповідь, підкреслюючи, що карта працює з копією оригінальних елементів, залишаючи оригінальний масив взагалі недоторканим
Jesús Franco

7

Ви можете зателефонувати за кожного так:

forEachбуде повторюватися над наданим вами масивом, і для кожної ітерації він буде мати значення, elementяке має значення цієї ітерації. Якщо вам потрібен індекс, ви можете отримати поточний індекс, передавшиi якості другого параметра функцію зворотного виклику для ForEach.

Foreach - це в основному функція високого порядку, яка приймає іншу функцію в якості свого параметра.

let theArray= [1,3,2];

theArray.forEach((element) => {
  // Use the element of the array
  console.log(element)
}

Вихід:

1
3
2

Ви також можете повторювати такий масив:

for (let i=0; i<theArray.length; i++) {
  console.log(i); // i will have the value of each index
}

6

Якщо ви хочете провести цикл через масив об'єктів за допомогою функції стрілки:

let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}];

arr.forEach((person)=>{
  console.log('I am ' + person.name + ' and I am ' + person.age + ' old');
})


6

Синтаксис лямбда зазвичай не працює в Internet Explorer 10 або нижче.

Я зазвичай використовую

[].forEach.call(arrayName,function(value,index){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

Якщо ви фанат jQuery і у вас вже працює файл jQuery, вам слід змінити положення параметрів індексу та значення.

$("#ul>li").each(function(**index, value**){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

5

Якщо у вас є масивний масив, ви повинні використовувати iteratorsдля отримання деякої ефективності. Ітератори властивість деяких колекцій JavaScript (як Map, Set, String, Array). Навіть for..ofвикористовує iteratorпід кришкою.

Ітератори підвищують ефективність, дозволяючи споживати елементи в списку один за одним, як ніби вони є потоком. Що ітератора робить особливим, це те, як він проходить колекцію. Інші петлі повинні завантажувати всю колекцію вперед, щоб повторити її, тоді як ітератору потрібно лише знати поточну позицію в колекції.

Ви можете отримати доступ до поточного елемента, викликавши nextметод ітератора . Наступний метод поверне valueпоточний елемент та а, booleanщоб вказати, коли ви досягли кінця колекції. Далі наведено приклад створення ітератора з масиву.

Перетворіть свій звичайний масив в ітератор, використовуючи такий values()метод:

    const myArr = [2,3,4]

let it = myArr.values();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Ви також можете перетворити свій звичайний масив в ітератор, використовуючи Symbol.iteratorтакий:

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Ви також можете перетворити свій звичайний arrayу iteratorтакий:

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

ПРИМІТКА :

  • Ітератори є вичерпними за своєю природою.
  • Об'єкти iterableза замовчуванням не є . Використовуйте for..inв цьому випадку, оскільки замість значень він працює з ключами.

Більше про це можна прочитати iteration protocol тут .


5

Якщо ви хочете використовувати forEach(), це буде виглядати так:

theArray.forEach ( element => {
    console.log(element);
});

Якщо ви хочете використовувати for(), це буде виглядати так:

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}

4

Відповідно до нової оновленої функції ECMAScript 6 (ES6) та ECMAScript 2015, ви можете використовувати наступні параметри з циклами:

для петель

for(var i = 0; i < 5; i++){
  console.log(i);
}

// Output: 0,1,2,3,4

для ... в петлях

let obj = {"a":1, "b":2}

for(let k in obj){
  console.log(k)
}

// Output: a,b

Array.forEach ()

let array = [1,2,3,4]

array.forEach((x) => {
  console.log(x);
})

// Output: 1,2,3,4

для ... петель

let array = [1,2,3,4]

for(let x of array){
  console.log(x);
}

// Output: 1,2,3,4

при цьому петлі

let x = 0

while(x < 5){
  console.log(x)
  x++
}

// Output: 1,2,3,4

робити ... поки петлі

let x = 0

do{
  console.log(x)
  x++
}while(x < 5)

// Output: 1,2,3,4

4

Продуктивність

Сьогодні (2019-12-18) я виконую тест на своїх macOS v10.13.6 (High Sierra), на Chrome v 79.0, Safari v13.0.4 та Firefox v71.0 (64 біт) - висновки про оптимізацію (і мікрооптимізацію, яка зазвичай не варто вводити його в код, оскільки користь невелика, але складність коду зростає).

  • Схоже, що традиційний for i( Aa ) - хороший вибір, щоб написати швидкий код у всіх браузерах.

  • Інші рішення, як-от for-of( Ad ), всі в групі C. ... зазвичай у 2 - 10 (і більше) разів повільніші, ніж Aa , але для малих масивів нормально їх використовувати - задля збільшення ясності коду.

  • Цикли із збереженою довжиною масиву n( Ab, Bb, Be ) іноді швидші, іноді ні. Ймовірно, компілятори автоматично виявляють цю ситуацію і вводять кешування. Різниці в швидкості між кешованою та без кешованою версіями ( Aa, Ba, Bd ) приблизно ~ 1%, тому, схоже, введення n- це мікрооптимізація .

  • i--, Як розчини , де цикл починається з останнього елемента масиву ( Ac, Bc ), як правило , ~ 30% повільніше , ніж вперед рішень - ймовірно, причина , це шлях пам'яті процесора кеш роботи - вперед читання пам'яті є більш оптимальним для кешування процесора). Не рекомендується використовувати такі рішення.

Деталі

У тестах ми обчислюємо суму елементів масиву. Я виконую тест на малі масиви (10 елементів) та великі масиви (елементи 1М) та поділяю їх на три групи:

  • А - forтести
  • Б - whileтести
  • C - інші / альтернативні методи

Перехресні результати браузера

Результати для всіх перевірених браузерів

Введіть тут опис зображеннябраузери **

Масив з 10 елементами

Результати для Chrome. Ви можете виконати тест на своїй машині тут .

Введіть тут опис зображення

Масив з 1 000 000 елементів

Результати для Chrome. Ви можете виконати тест на своїй машині тут

Введіть тут опис зображення


4

Підсумок:

Під час ітерації через масив ми часто хочемо досягти однієї з наступних цілей:

  1. Ми хочемо перебрати масив і створити новий масив:

    Array.prototype.map

  2. Ми хочемо перебрати масив і не створити новий масив:

    Array.prototype.forEach

    for..of петля

У JavaScript існує багато способів досягнення обох цих цілей. Однак деякі зручніші за інші. Нижче ви можете знайти кілька часто використовуваних методів (найзручніший IMO) для виконання ітерації масиву в JavaScript.

Створення нового масиву: Map

map()це функція, розташована за допомогою Array.prototypeякої можна перетворити кожен елемент масиву, а потім повернути новий масив. map()приймає в якості аргументу функцію зворотного виклику і працює таким чином:

let arr = [1, 2, 3, 4, 5];

let newArr = arr.map((element, index, array) => {
  return element * 2;
})

console.log(arr);
console.log(newArr);

Зворотний виклик, який ми передали map()як аргумент, виконується для кожного елемента. Потім повертається масив, який має таку ж довжину, що і вихідний масив. У цьому новому елементі масиву перетворюється функція зворотного виклику, передана як аргументmap() .

Відмінна відмінність між mapта іншим механізмом циклу, як forEachі for..ofциклом, полягає в тому, що mapповертає новий масив і залишає старий масив неушкодженим (за винятком випадків, коли ви явно маніпулюєте ним за допомогою думок подібних splice).

Також зауважте, що mapзворотний виклик функції дає номер індексу поточної ітерації як другий аргумент. Крім того, чи містить третій аргумент масив, на якому mapвикликався? Іноді ці властивості можуть бути дуже корисними.

Цикл використання forEach

forEachце функція, яка розташована Array.prototypeі приймає функцію зворотного дзвінка як аргумент. Потім він виконує цю функцію зворотного виклику для кожного елемента масиву. На відміну від map()функції, функція forEach нічого не повертає ( undefined). Наприклад:

let arr = [1, 2, 3, 4, 5];

arr.forEach((element, index, array) => {

  console.log(element * 2);

  if (index === 4) {
    console.log(array)
  }
  // index, and oldArray are provided as 2nd and 3th argument by the callback

})

console.log(arr);

Як і mapфункція, forEachзворотний виклик надає номер індексу поточної ітерації як другий аргумент. Також, чи містить третій аргумент масив, на якому forEachвикликався?

Проведіть циклічні елементи за допомогою for..of

for..ofПетлі петлі через кожен елемент масиву (або будь-якого іншого об'єкта Iterable). Це працює наступним чином:

let arr = [1, 2, 3, 4, 5];

for(let element of arr) {
  console.log(element * 2);
}

У наведеному вище прикладі elementрозшифровується елемент масиву і arrє масив, який ми хочемо циклічно. Зауважте, що ім'я elementдовільне, і ми могли вибрати будь-яке інше ім’я, наприклад, 'el' або щось більш декларативне, коли це застосовується.

Не плутайте for..inпетлю з for..ofциклом. for..inбуде перебирати всі перелічені властивості масиву, тоді як for..ofцикл буде проходити лише через елементи масиву. Наприклад:

let arr = [1, 2, 3, 4, 5];

arr.foo = 'foo';

for(let element of arr) {
  console.log(element);
}

for(let element in arr) {
  console.log(element);
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.