Почему не стоит использовать цикл ‘for in’ в JavaScript?

JavaScript, Вопросы и ответы
29 февраля 2012

Вопрос

Я где-то читал, что цикл ‘for in’ в JavaScript «не рекомендуется к применению». Но я забыл (или там не было указано), почему его использование - не очень хорошая идея. Если это действительно так, объясните, что плохого в использовании цикла ‘for in’, какие подвохи он в себе таит, и по возможности подкрепите своё мнение реальными примерами.


Ответ №1

Причина - в его поведении. Вот как работает обычный цикл ‘for’:

var a = [];
a[5] = 5; // Полностью верный Javascript-код, который создаёт массив
for (var i=0; i<a.length; i++) {
    // Перебирает все целочисленные индексы массива от 0 до 5, как и ожидается
}

А теперь о том, как поведёт себя цикл ‘for in’ в этом случае:

var a = [];
a[5] = 5;
for (x in a) {
    // Цикл выполнится только один раз для 5
    // и проигнорирует индексы 0-4
}

Также учтите, что библиотеки Javascript (типа MooTools) часто делают подобные вещи, которые будут иметь отношение ко всем массивам, которые Вы создаёте:

// Где-то глубоко в какой-нибудь Javascript-библиотеке:
Array.prototype.foo = 1;
// Попробуйте угадайте, что сделает следующий код:
var a = ['a','b','c','d','e'];
for (var x in a){
    // Сейчас ‘foo’ является частью ЛЮБОГО массива
    // и отобразится здесь как значение переменной ‘x’
    console.log(x); // Выводит 0, 1, 2, 3, 4, foo (!!!)
}

Для избежания этой проблемы можно поступить следующим образом:

for (var x in a) {
    // Отсеиваем здесь свойства не принадлежащие ‘a’
    if (a.hasOwnProperty(x)) {
       console.log(x); // Получим 0, 1, 3, 3, 4
    }
}

Некоторые даже рекомендуют вызывать этот метод прямо из Object.prototype, чтобы избежать проблем в том случае, если кто-то добавит свойство с именем hasOwnProperty к Вашему объекту, хотя это и маловероятно.

for (var x in a) {
    if (Object.prototype.hasOwnProperty.call(a, x)) {
        console.log(x); // Получим 0, 1, 3, 3, 4
    }
}

Также поведение этого цикла может быть непредсказуемым в старых браузерах, таких как IE 8 и ниже:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';
for (var p in array) {
   // В этом месте ‘p’ будет 2, 1 а затем 0 (в IE8 и ниже)
}

При использовании цикла ‘for in’ всегда используйте ключевое слово var перед значением ключа, так Вы обезопасите себя от переназначения соответствующей переменной, если она встречается в глобальном контексте.

Вывод: использовать этот цикл можно, просто надо делать это с осторожностью.

2 комментария

  • IN:

    мдя….
    var a = [];
    a[5] = 5;
    for (x in a) {
    // Цикл выполнится только один раз для 5
    // и проигнорирует индексы 0-4
    }

    и что здесь некорректного?
    Назовите хоть один язык программировангия, для которого подобный цикл выполнится больше одного раза…..

    • Администратор:

      Да, но ведь a.lenght в случае JavaScript будет равно 6, поэтому интуитивно ожидается, что будет 6 итераций. Не?
      В других языках (по крайней мере PHP) длина массива в аналогичном случае будет 1.

Добавить комментарий


(обязательно)