JavaScript для ... в сравнении с


461

Как вы думаете, есть большая разница в циклах for ... in и for? Какое «для» вы предпочитаете использовать и почему?

Допустим, у нас есть массив ассоциативных массивов:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Итак, мы можем повторить:

for (var i = 0; i < myArray.length; i++)

А также:

for (var i in myArray)

Я не вижу большой разницы. Есть ли проблемы с производительностью?


13
Обратите внимание , что мы также, от JS 1.6 , имеем: myArray.forEach(callback[, thisarg]).
Бенджи XVI

14
@Benji array.forEach на самом деле в ES5.
mikemaccana

2
в цикле for-in вам нужно условие, которое выглядит следующим образом:if(myArray.hasOwnProperty(i)){true}
Эрик Ходонски

6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); можно использовать практически везде, кроме IE8 - и это, безусловно, самый элегантный синтаксис
Jon z

5
Есть также for...ofзаявление в ECMAScript 6 , например:for (let i of myArray) console.log(i);
Виталий Федоренко

Ответы:


548

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

Массив повторяется с использованием:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Объект, используемый в качестве ассоциативного массива, повторяется с использованием:

for (var key in o)
  //do stuff with o[key]

Если у вас нет причин разрушать землю, придерживайтесь установленной схемы использования.


38
Следует отметить, что это хорошая практика для использования ... с фильтрующим оператором if. Существует удобный метод Object «obj.hasOwnProperty (member)», который проверяет, является ли член, возвращаемый итератором, действительно членом объекта. См .: javascript.crockford.com/code.html
Дамир Зекич

57
Как прокомментировано в другом ответе (-ях), «for ... in» не работает правильно для массивов, так как будет перебирать все свойства и методы массива. Таким образом, вы должны использовать «for ... in» только для перебора свойств объекта. В противном случае придерживайтесь «for (i = 0; i <что-то; i ++)»
Денилсон Са Майя

По соображениям производительности, IMO лучше оценивать длину массива перед for, а не оценивать a.length каждый раз в цикле.
UpTheCreek,

9
@UpTheCreek: Это действительно так, когда массив фактически является чем-то, возвращаемым HTMLDOM, однако мне интересно, насколько большим должен быть стандартный массив javascript, прежде чем вы сможете увидеть заметную разницу? Лично я бы держал код настолько простым, насколько это было возможно, до тех пор, пока он не стал необходим, чтобы сделать что-то другое.
AnthonyWJones

2
@Pichan Я думаю, ты имеешь в виду i < l, что нет i < a, в твоем состоянии цикла.
Макс Нанаси

161

Дуглас Крокфорд рекомендует в JavaScript: Хорошие детали (стр. 24) избегать использования этого for inутверждения.

Если вы используете for inдля циклического перебора имен свойств в объекте, результаты не упорядочены. Хуже: вы можете получить неожиданные результаты; в него входят члены, унаследованные от цепочки прототипов, и имена методов.

Все, кроме свойств, может быть отфильтровано .hasOwnProperty. Этот пример кода делает то, что вы, вероятно, хотели изначально:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}

70
for ... in идеально подходит для зацикливания свойств объекта. Это не подходит для зацикливания элементов массива. Если вы не можете понять разницу между этими сценариями, тогда да, вам следует избегать ... in; в противном случае сходите с ума.
Shog9

8
Хочу подчеркнуть тот факт, что он НЕ ЗАКАЗАН! Это может быть большой проблемой, и ее будет трудно обнаружить.
Джейсон

4
+1 за «это включает членов, унаследованных от цепочки прототипов и названия методов». Вам будет весело, например, если кто-то использует ваш код с загруженным Prototype (даже если ваш код его не использует).
IJW

13
Пожалуйста , не забудьте объявить о nameпеременной: for(var name in object)..., в противном случае, если этот код находится внутри функции, например, то nameпеременная в конечном итоге свойство глобального объекта (присваивание необъявленной идентификатора делает это), а также в новом ECMAScript 5 Строгий режим, этот код будет выбрасывать ReferenceError.
CMS

6
@Nosredna: Существует проблема с порядком итераций для Chrome, поданная не кем иным, как Джоном Резигом, которая помечена как WontFix. code.google.com/p/chromium/issues/detail?id=883 . Даже до Chrome порядок итерации во всех браузерах был неодинаковым, если вы удалите, а затем снова добавите свойство. Также IE 9 ведет себя во многом как хром (предположительно для улучшения скорости). Так что ... Пожалуйста, прекратите распространять неточную информацию, вам будет очень наивно полагаться на нее.
Хуан Мендес

62

FYI - jQuery пользователи


each(callback)Метод jQuery for( ; ; )по умолчанию использует цикл и будет использовать for( in ) только если длина равна undefined.

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

Пример :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

Недостатком использования этого является то, что если вы выполняете какую-то логику, не связанную с пользовательским интерфейсом, ваши функции будут менее переносимы к другим средам. Эта each()функция, вероятно, лучше всего зарезервирована для использования с селекторами jQuery и for( ; ; )может быть рекомендована в противном случае.



4
Там всегда есть documentcloud.github.com/underscore, в котором есть _.each и множество других полезных функций
w00t

1
это также означает, что если у меня есть свойство length в моем объекте, $ .each потерпит неудачу? например, x = {a: "1", b: "2", длина: 3}.
Онур Топал

29

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

Например:

for (var i = myArray.length-1; i >= 0; i--)

почти в два раза быстрее в некоторых браузерах, чем:

for (var i = 0; i < myArray.length; i++)

Однако, если ваши массивы ОГРОМНЫ или вы не зацикливаете их постоянно, все достаточно быстро. Я серьезно сомневаюсь, что зацикливание массивов является узким местом в вашем проекте (или для любого другого проекта в этом отношении)


5
Сохранит ли myArray.length в переменной перед циклом разницу в производительности? Мое предположение "да".
Томалак

3
Нет. MyArray.length - это свойство, а не метод объекта - для определения его значения не выполняется никаких вычислений. Сохранение значения в переменной ничего не изменит.
Джейсон

3
Да, есть. Свойства не являются переменными; у них есть код get / set.
2010 года

12
Я склонен использоватьfor(var i = myArray.length; i--;)
Кевин

2
Код для наглядности и читабельности. Не то, что происходит в некоторых браузерах немного быстрее при использовании абсурдно массивных массивов в текущий момент времени. Оптимизация может измениться, но читабельность вашего кода (или ее отсутствие) не изменится. Напишите код, за которым другие могут легко следовать, и дайте оптимизаторам наверстать упущенное в свое время.
аромат

26

Обратите внимание, что родной метод Array.forEach теперь широко поддерживается .


2
Что оно делает? Есть ли у него проблемы, упомянутые в других постах (перебирая свойства вместо элементов массива)? Кроме того, поскольку IE8 не поддерживает его, довольно сложно сказать, что он широко поддерживается.
Рауни Лиллемец

2
Как бы ни пренебрегали пользователи * nix, IE8 больше всего относится к пользователям под windows7. это огромная часть рынка браузеров.
sbartell

2
@Rauni - я принимаю вашу точку зрения, но для настольных устройств доля браузера IE составляет менее 40%, согласно en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table , а также согласно marketshare.hitslink.com/… и другим сайтам, по крайней мере 8% браузеров имеют IE 9. Другими словами, Array.forEach поддерживается примерно 70% браузеров для настольных компьютеров, поэтому я не думаю, что «широко поддерживаемая» нецелесообразна. Я не проверял, но мобильная поддержка (в браузерах WebKit и Opera) может быть даже выше. Очевидно, что есть значительные различия географически.
Сэм Даттон

1
Спасибо за обновления. Я согласен, что можно сказать, что это "широко поддерживается". Единственная проблема заключается в том, что если пользователь использует этот метод JS, он / она все еще должен написать метод резервного копирования для случая, если он не поддерживается ..
Рауни Лиллеметс

1
@Rauni - вы можете использовать es5-shim и es6-shim для автоматического предоставления методов резервного копирования. github.com/es-shims/es5-shim
Мишель ван дер Блонк

24

Обновленный ответ за 2012 год текущей версии всех основных браузеров - Chrome, Firefox, IE9, Safari и Opera поддерживают встроенный в ES5 массив .forEach.

Если у вас нет причин поддерживать нативную поддержку IE8 (имея в виду, что этим пользователям могут быть предоставлены рамки ES5-shim или Chrome, что обеспечит надлежащую среду JS), проще использовать правильный синтаксис языка:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

Полная документация для array.forEach () находится в MDN.


1
Вы должны действительно задокументировать параметры обратного вызова: 1-ое значение элемента, 2-е значение индекса элемента, 3-е значение - пройденный массив
drewish

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

1
@Cory: ES5 forEach можно легко добавить в устаревшие браузеры ES3. Чем меньше кода, тем лучше код.
mikemaccana

2
@nailer Может ли это использоваться на массивах и объектах взаимозаменяемо?
Hitautodestruct

1
@hitautodestruct Это часть прототипа массива, а не объекта. Обычно в сообществе объекты, не являющиеся массивами, по-прежнему итерируются с помощью 'for (var key in object) {}'.
mikemaccana

14

Два не совпадают, когда массив разреженный.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}

14

Использование forEach для пропуска цепочки прототипов

Просто быстрое дополнение к ответу @ nailer выше , использование forEach с Object.keys означает, что вы можете избежать итерации по цепочке прототипов без использования hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});

2
блин, это подлый Стоило прочитать 50 других постов, чтобы добраться до этого. obj = {"розовый": "утки", красный: "гуси"}; Object.keys (obj) === ["pink", "red"]
Орвеллофил

14

Я полагаю, что вы должны выбрать метод итерации в соответствии с вашими потребностями. Я хотел бы предложить вам на самом деле не когда - либо проходной выходец Arrayс for inструктурой. Это намного медленнее и , как указывал Чейз Сейберт недавно, не совместимо с платформой Prototype.

Существует отличный эталон для различных стилей зацикливания, на которые вам обязательно следует обратить внимание, если вы работаете с JavaScript . Не делайте ранних оптимизаций, но вы должны держать эти вещи где-то в затылке.

Я бы использовал, for inчтобы получить все свойства объекта, что особенно полезно при отладке ваших скриптов. Например, мне нравится иметь эту строку под рукой, когда я исследую незнакомый объект:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Он выгружает содержимое всего объекта (вместе с телами методов) в мой журнал Firebug. Очень удобно


Ссылка теперь не работает. Конечно, хотелось бы увидеть эталон, если у кого-то есть другая ссылка.
Billbad

Это больше не сломано.
Олли

Разрыв циклов по каждому элементу? Как сейчас принято считать, это должен решить прототип.
mvrak

7

вот что я сделал

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

это то, как вы будете использовать его,
это будет работать с массивами и объектами (такими как список элементов HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Я просто сделал это, поэтому я открыт для предложений :)


Отличный материал - довольно просто!
Крис

6

Я бы использовал разные методы, основанные на том, как я хотел ссылаться на элементы.

Используйте foreach, если вы просто хотите текущий элемент.

Используйте для, если вам нужен индексатор для сравнительных сравнений. (Т.е. как это соотносится с предыдущим / следующим пунктом?)

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


Смотрите ответ Bnos ниже - потому что ... не делает то, что вы ожидаете здесь, и если вы используете его, вы вполне можете получить все виды удовольствия. Для протокола, Prototype делает все правильно .
marcus.greasly

4

С помощью for (var i в myArray) вы также можете перебирать объекты, я буду содержать имя ключа и вы можете получить доступ к свойству через myArray [i] . Кроме того, любые методы, которые вы добавите к объекту, также будут включены в цикл, т. Е. Если вы используете какую-либо внешнюю среду, такую ​​как jQuery или prototype, или если вы добавляете методы непосредственно в прототипы объектов, в какой-то момент я укажу на эти методы.


4

Осторожно!

Если у вас есть несколько тегов сценария, и вы ищете информацию в атрибутах тега, например, вы должны использовать свойство .length с циклом for, потому что это не простой массив, а объект HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Если вы используете оператор foreach для (var i в yourList), он вернет свойства и методы HTMLCollection в большинстве браузеров!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Даже если getElementsByTagName должен возвращать NodeList, большинство браузеров возвращают HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName


3

Ибо в циклах на массивах не совместим с прототипом. Если вы думаете, что вам может понадобиться использовать эту библиотеку в будущем, имеет смысл придерживаться циклов for.

http://www.prototypejs.org/api/array


Забудьте «вам может понадобиться использовать эту библиотеку». Вместо этого подумайте: «Ваш JS может быть включен во все, что использует эту библиотеку», потому что проблемы все еще возникают.
IJW

3

Я видел проблемы с «для каждого» с использованием объектов и прототипов и массивов

я понимаю, что для каждого для свойств объектов, а не массивов


3

Если вы действительно хотите ускорить свой код, как насчет этого?

for( var i=0,j=null; j=array[i++]; foo(j) );

это своего рода логика while внутри оператора for, и она менее избыточна. Также Firefox имеет Array.forEach и Array.filter


2
Почему это ускорит ваш код? Я не понимаю, почему такие заявления могут ускорить процесс.
Rup

3

Более короткий и лучший код в соответствии с jsperf

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}

1

Используйте цикл Array (). ForEach, чтобы воспользоваться преимуществами параллелизма.


4
JavaScript в браузере является параллельным циклом обработки событий, поэтому Array.prototype.forEachне будет выполнять несколько вызовов обратного вызова параллельно.
Майк Сэмюэль

1

для (;;) для массивов : [20,55,33]

for..in для объектов : {x: 20, y: 55: z: 33}


0

Быть осторожен!!! Я использую Chrome 22.0 в Mac OS, и у меня проблемы с каждым синтаксисом.

Я не знаю, если это проблема браузера, проблема JavaScript или какая-то ошибка в коде, но это ОЧЕНЬ странно. Снаружи объекта работает отлично.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works

0

Между ними есть важное различие. For-in перебирает свойства объекта, поэтому, когда case является массивом, он будет перебирать не только его элементы, но и функцию «удалить», которая у него есть.

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

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Вы можете использовать for-in с if(myArray.hasOwnProperty(i)). Тем не менее, при переборе массивов я всегда предпочитаю избегать этого и просто использовать оператор for (;;).


0

Хотя они оба очень похожи, есть небольшая разница:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

в этом случае вывод:

СТАНДАРТ ДЛЯ ПЕТЛИ:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

в то время как в этом случае вывод:

ВНЕШНЯЯ ПЕТЛЯ:

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123


0

Оператор for позволяет выполнять циклический просмотр имен всех свойств объекта. К сожалению, он также проходит по всем элементам, унаследованным через цепочку прототипов. У этого есть плохой побочный эффект обслуживания функций метода, когда интерес к членам данных.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.