Питання Для кожного по масиву в JavaScript?


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

Я думав, що це щось на зразок цього:

forEach(instance in theArray)

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


3791
2018-02-17 13:51


походження


Я шукав це, але я шукав forEach  і не просто for. як сказано, в c # було трохи інше, і це мене плутало :) - Dante1986
ECMAScript & nbsp; 6 може містити "для ... з" конструкції. Побачити для ... від (MDN) для отримання додаткової інформації. Ви вже можете спробувати це з останніми версіями Firefox. - Slaven Rezic
Array.ForEach приблизно на 95% повільніше, ніж для () для кожного для масиву в JavaScript. Перегляньте цей тест продуктивності в Інтернеті: jsperf.com/fast-array-foreach через coderwall.com/p/kvzbpa - molokoloco
Для багатьох ситуацій, що на 95% повільніше, не буде суттєво blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html - David Sykes
Навпаки, в Росії Python Це є більш ефективний використовувати функції, ніж використовувати традиційні для циклів. (Розглянемо це i < len і i++ може бути виконаний двигуном, а не перекладачем.) - joeytwiddle


Відповіді:


TL; DR

  • Не використовуйте for-in якщо ви не використовуватимете його з гарантіями або хочете знати, чому це може укусити вас.
  • Ваші найкращі ставки зазвичай

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

Але є багато більше досліджувати, читати далі ...


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

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

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

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

У вас є три варіанти 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); // Would cause "ReferenceError: index is not defined"
//console.log(value); // Would cause "ReferenceError: value is not defined"

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

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        alert("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 будеш дотримуватися цього замовлення. (Детальніше в це інша відповідь.)

Все-таки це може бути корисним, особливо для рідкий масиви, якщо ви використовуєте відповідні гарантії:

// `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 are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

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

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

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

Це трохи додаткових накладних витрат на цикл ітерації на більшості масивів, але якщо у вас є рідкий масив, це може бути більш ефективним способом циклу, тому що він тільки цикли для записів, які насправді існують. Наприклад, для вищезазначеного масиву ми п'ємо в цілому тричі (для ключів "0", "10", і "10000"- пам'ятайте, це рядки), а не 10 001 разів.

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

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

І тоді ми використали б це так:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

Або якщо вас цікавить лише тест "досить добре для більшості випадків", ви можете використовувати це, але поки він закритий, це не зовсім правильно:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

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

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

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

Вихід:

a
б
с

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

5. Явним чином використовуйте ітератор (ES2015 +)

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

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

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

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

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

Для об'єктів з масивом

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

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

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

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

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

    Припустимо, що ви хотіли використати forEach на Nodeс 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);
    

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

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

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

  2. Використовуйте розповсюдження синтаксису (...)

    Також можна використовувати ES2015 поширювати синтаксис з двигунами JavaScript, які підтримують цю функцію:

    var trueArray = [...iterableObject];
    

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

    var divs = [...document.querySelectorAll("div")];
    
  3. Використовуйте Array.from  (специфікація) | (MDN)

    Array.from (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 функції з host-provided масивні об'єкти (списки DOM та інші речі, надані браузером, а не движком JavaScript), потрібно обов'язково перевірити тестування у цільових середовищах, щоб переконатися, що об'єкт, що надається хостом, поводиться належним чином. Більшість з них поводяться належним чином (зараз), але важливо тестувати. Причина в тому, що більшість з Array.prototype методи, які ви, швидше за все, захочете використовувати, покладаються на об'єкт, що надається хостом, що дає чесну відповідь на абстрактне [[HasProperty]] операція На даний момент браузери роблять дуже хорошу роботу, але специфікація 5.1 допускає можливість того, що об'єкт, що надається хостом, може бути несправедливим. Це в §8.6.2, кілька абзаців під великим столом біля початку цього розділу), де він говорить:

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

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


5980
2018-02-17 13:53



Я також хотів би додати це .forEach не може бути ефективно порушено. Ви повинні виключити, щоб виконати перерву. - Pijusn
@Pius: Якщо ви хочете зламати цикл, ви можете використовувати some. (Я б вважав за краще, щоб розбити forEach також, але вони, м, не запитували мене. ;-)) - T.J. Crowder
@ T.J.Crowder Правда, навіть якщо вона більше схожа на навколишнє середовище, оскільки це не є її головною метою. - Pijusn
@ user889030: вам потрібен a , після k=0не а ;. Пам'ятайте, що програмування - багато речей, одним з яких є пильне увагу до деталей ... :-) - T.J. Crowder
@JimB: це викладено вище (і length це не метод). :-) - T.J. Crowder


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

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

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

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

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

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


451
2018-02-17 13:55



чому це for(instance in objArray)  не правильне використання? це виглядає мені простіше, але я чую, що ви говорите про це як не правильний спосіб використання? - Dante1986
Ви можете використовувати кешування вбудованої довжини: для (var i = 0, l = arr.length; i <l; i ++) - Robert
Чи є кома в кінці першої рядки навмисною, чи це помилка (може бути точкою з комою)? - 9KSoft
@ wardha-web Це навмисно. Це дозволяє нам оголосити кілька змінних одним varключове слово Якщо б ми використовували точку з комою, то element було б оголошено в глобальному масштабі (або, скоріше, JSHint б крикнув у нас, перш ніж він досяг виробництва). - PatrikAkerstrand


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

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

EDIT: 

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

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

205
2018-02-17 14:01



Я, мабуть, збираюся використовувати цю відповідь найчастіше. Це не найкраща відповідь на питання, але на практиці це буде найпростішим і найбільш придатним для тих, хто використовує jQuery. Я думаю, що всі повинні навчитися і ванільному шляху. Це ніколи не болить розширення вашого розуміння. - mopsyd
Саме заради цього: jQuery кожен набагато повільніше, ніж рідні рішення. JQuery рекомендує користуватися рідним JavaScript замість jQuery, коли це можливо. jsperf.com/browser-diet-jquery-each-vs-for-loop - Kevin Boss
Утримайтеся від використання jQuery, коли ви можете використовувати ваніль js - Noe
Дотримуйтесь стандартної JS, тримайте сторонні бібліотеки поза відповіддю, якщо не існує рідної мови - Steve K
Щось нагадує мені про це: i.stack.imgur.com/ssRUr.gif - Ajedi32


Петля назад

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

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

Переваги:

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

Недоліки:

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

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

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

Хоча переваги продуктивності зазвичай незначні, це нагадує крики:

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

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

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

Краще використовувати для кожного ()

Загалом для коду вищого рівня де ясність і безпека Більше проблем, я б рекомендував використовувати Array::forEach як ваш за замовчуванням шаблон:

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

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

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


Як це працює?

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 що дозволяє уникнути будь-яких проблем Масив-поза межами  undefined предмети

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

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

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

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

    [5, 5, 4]

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

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


Дрібниці

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

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

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


88
2018-05-02 14:21



Я забув додати орієнтири. Я також забув згадати, як зворотний цикл є значною оптимізацією на 8-бітних процесорах, таких як 6502, де ви дійсно отримуєте порівняння безкоштовно! - joeytwiddle
Як щодо цього для зворотного циклу? var i = array.length; поки я--) { ... - Kabb5
Вибачте @ Kabb5 Я був AFK. Так, ця петля цілком зрозуміла і по суті однакова. - joeytwiddle
Ага, старе старе "стрілка та віконця" кодова структура. Я це люблю! +1 від мене! - Matheus Avellar
Той же відповідь дається набагато лаконічно тут (на інше питання). - joeytwiddle


Дещо С- використання мов стилів 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 щоб забезпечити петлі, які можуть бути короткозамкненими.


71
2018-02-17 14:00



Отже, як вийти з схеми ECMAScript5 для кожного циклу, як ми хотіли б для нормальної для циклу або foreach циклу, як це знайдено в мовах C-стилі? - Ciaran Gallagher
@ CiaranG, в JavaScript це загальноприйняте для перегляду each методи, які дозволяють return false щоб вирватися з циклу, але з forEach це не вибір. Можна використовувати зовнішній прапор (тобто if (flag) return;, але це тільки запобігає виконанню решти функціонального органу, forEach буде продовжувати повторювати всю колекцію. - zzzzBov


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

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

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


30
2018-02-17 13:55



для (var i = 0, length = myArray.length; i <length; i ++) повинен зробити це - Edson Medina
@EdsonMedina Це також створить нову глобальну змінну, яка викликається length. ;) - joeytwiddle
@joeytwiddle Так, але це виходить за межі цього повідомлення. Ви все-таки створите глобальну змінну. - Edson Medina
@EdsonMedina Мої вибачення, я мав це зовсім неправильно. Використовуючи , після виконання завдання ні введіть новий глобальний, так що ваша пропозиція справедлива добре! Я заплутував це для іншої проблеми: Використовуючи = після призначення творить новий глобальний. - joeytwiddle
Остерігайтеся змінної i є ні локальний до петлі. JavaScript не має областей блокування. Ймовірно, краще оголосити var i, length, arrayItem; перед циклом, щоб уникнути цього непорозуміння. - James


А. для кожного реалізація (див. в 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
  }
);

25
2018-04-10 00:26



ітератор в цьому, робить розрахунок непотрібної довжини. У ідеальному випадку довжина списку слід обчислити лише один раз. - MIdhun Krishna
@MIdhunKrishna Я оновив свою відповідь і jsFiddle, але майте на увазі, що це не так просто, як ви думаєте. Перевір це питання - nmoliveira
Повне і правильне впровадження можна знайти тут: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - marciowb


Якщо ви не заперечуєте спорожнення масиву:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

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


25
2018-03-10 02:37



Це не спрацьовує, якщо вам трапиться розріджений масив [1, 2, undefined, 3]. - M. Grzywaczewski
... або навіть щось неправдиве: [1, 2, 0, 3] або [true, true, false, true] - joeytwiddle


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

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

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

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 цикл. Тепер я досягну результативності в розділі "мінуси", оскільки є для кожних циклів багато чого повільніше, ніж для петлі. Я згадую це як про, адже приємно бути послідовним та стандартизованим.

Розглянемо наступні 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 в цьому поганому хлопчику.


25
2018-06-20 22:56





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

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

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

Дивись також


24
2017-07-17 09:07