Чому "for (var item in list)" з масивами вважається поганою практикою в JavaScript?


78

Дано простий нульовий масив із числовим індексом:

var list = ['Foo', 'Bar', 'Baz'];

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

for(var item in list) { ... }

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

var count = list.length;

for(var i = 0; i < count; i++) {
    var item = list[i];
    ...
}

У чому причина не використовувати простішу версію вище та використовувати другий приклад?


Ви не зациклюєтесь на елементах із цим циклом, ви переглядаєте ключі / назви властивостей / індиє.
Бергі,

5
Також кодери C у вікторіанських топах не розуміють ітераторів. Однак майте на увазі його виплювання ключів, а не значень. Формат for (;;) є швидшим, але в 99% випадків це насправді не має значення. Час кодування дорожчий за обчислювальний, якщо ви не працюєте над мегапроектами чи матеріалами, які справді потребують оптимізації.
Shayne

Відповіді:


96

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

По-друге, здійснюється for...inітерація над усіма переліченими властивостями об’єкта, включаючи ті, що успадкували від його прототипу. У випадку масивів це може вплинути на вас, якщо ваш код або будь-яка бібліотека, включена на вашу сторінку, доповнила прототип Array, що може бути справді корисною справою:

Array.prototype.remove = function(val) {
    // Irrelevant implementation details
};

var a = ["a", "b", "c"];

for (var i in a) {
    console.log(i);
}

// Logs 0, 1, 2, "remove" (though not necessarily in that order)

2
це прекрасно, поки ти використовуєш, hasOwnPropertyхоча - for (var i in a) { if (a.hasOwnProperty(i)) console.log(i); }-> 1 2 3
Dimitar Christoff

11
Одна пропозиція, змініть over all propertiesна over **enumerable** properties. У нових реалізаціях javascript властивості можна визначити за допомогою enumerableатрибута, встановленого false. Ці властивості та властивості вбудованих об'єктів javascript не відображатимуться у for...in.
Andy E

4
@Dimitar: Справді, хоча, коли ви це вже додали, цикл перестав виглядати простішим, ніж стандартний C-стиль для циклу.
Тім Даун

1
у будь-якому випадку - єдиною законною причиною циклу такого масиву є отримання ключів масиву (що працює через природу масивів у javscript) - будь-яка інша причина, вам краще зробити звичайний цикл.
Dimitar Christoff

19

Швидкість?

for(..;..;..)цикл виявився в 36 разів швидшим, ніж for .. in коли я тестував його тут.

Посилання надано люб'язно цю відповідь SO


5
Я неправильно прочитав, що нескінченний цикл в 36 разів швидший за звичайний цикл. Дякуємо за посилання +1.
Thomas O

@Thomas Розумне непорозуміння - коли його читають поза контекстом. Виправлено :)
Amarghosh

1

Якщо ви використовуєте для / in подібним чином, itemперераховує через рядкові значення "0", "1", ..., тож не фактичні об'єкти у списку. Отже, "елемент" у першому фрагменті більше схожий iна другий фрагмент, а не на item. Крім того, значення рядків перераховуються там, де ви очікували б числа. І у вас виникають проблеми, коли ви додаєте властивості до списку, наприклад array.ID = "a123", оскільки вони також будуть перераховані.

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


1

for ... in ... не повертає елементи списку, а замість цього перераховує властивості масиву.

Тільки з цієї причини він не може виступати як заміна for (i=0; i<arr.length; i++)циклу.

Відповідною альтернативою є for ... of ...конструкт. Він перераховує значення ітерабельного об'єкта, наприклад масиву. Детальніше про це можна прочитати в веб-документах MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

Це підтримується відповідними сучасними браузерами (Internet Explorer не враховується, його замінює Microsoft Edge). Якщо ви можете дозволити собі не підтримувати старі браузери, це, мабуть, шлях. Ви можете перевірити зручну таблицю підтримки браузера в кінці зв’язаної сторінки MDN, щоб побачити, які версії браузера насправді дозволяють for ... of ...використовувати.


Я був би дуже радий дізнатися, що ваше твердження про IE могло бути правдивим і довіреним :)
CapelliC

0

Додайте list.foo = bar;і спробуйте використовувати просте for. Якщо ви не використовуєте деякі бібліотеки (наприклад, prototypeJs) і не додаєте жодних нових властивостей до об’єкта масиву - ви можете використовувати простий оператор for-statement.

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