Как вы думаете, есть ли большая разница в циклах for ... in и for? Какие «для» вы предпочитаете использовать и почему?
Допустим, у нас есть массив ассоциативных массивов:
var myArray = [{'key': 'value'}, {'key': 'value1'}];
Итак, мы можем повторить:
for (var i = 0; i < myArray.length; i++)
И:
for (var i in myArray)
Я не вижу большой разницы. Есть ли проблемы с производительностью?
@Benji array.forEach на самом деле находится в ES5.
в цикле for-in вам понадобится условие, которое выглядит так: if (myArray.hasOwnProperty(i)){true}
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.info(element, index, array); }); можно использовать практически везде, кроме IE8, и это, безусловно, самый элегантный синтаксис.
Также в ECMAScript 6 есть инструкция for...of, например: for (let i of myArray) console.info(i);



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


С помощью для (var i в myArray) вы также можете перебирать объекты, я будет содержать имя ключа, и вы можете получить доступ к свойству через myArray [я]. Кроме того, любые методы, которые вы добавите к объекту, также будут включены в цикл, то есть, если вы используете какую-либо внешнюю структуру, такую как jQuery или prototype, или если вы добавляете методы непосредственно к прототипам объектов, в какой-то момент я будет указывать на эти методы.
Я бы использовал разные методы в зависимости от того, как я хотел ссылаться на элементы.
Используйте foreach, если вам нужен только текущий элемент.
Используйте for, если вам нужен индексатор для относительных сравнений. (То есть как это соотносится с предыдущим / следующим элементом?)
Разницы в производительности не замечал. Я бы дождался проблемы с производительностью, прежде чем беспокоиться об этом.
См. Ответ Bnos ниже - потому что ... in не делает то, что вы ожидаете здесь, и если вы его используете, вы вполне можете повеселиться. Для записи, Prototype действует так же, как Правильно.
Выбор должен основываться на том, какая идиома лучше всего понимается.
Массив повторяется с использованием:
for (var i = 0; i < a.length; i++)
//do stuff with a[i]
Объект, используемый как ассоциативный массив, повторяется с использованием:
for (var key in o)
//do stuff with o[key]
Если у вас нет серьезных причин, придерживайтесь установленной схемы использования.
Следует отметить, что это хорошая практика для использования for ... in с фильтрацией оператора if. Существует удобный метод объекта «obj.hasOwnProperty (member)», который проверяет, действительно ли член, возвращаемый итератором, является членом объекта. См .: javascript.crockford.com/code.html
Как прокомментировано в другом ответе (ах), «for ... in» не работает правильно для массивов, поскольку он будет перебирать все свойства и методы массива. Таким образом, вы должны использовать «for ... in» только для итерации по свойствам объекта. В противном случае придерживайтесь "for (i = 0; i <something; i ++)"
По соображениям производительности, IMO лучше оценивать длину массива перед for, а не оценивать a.length каждый раз в цикле.
@UpTheCreek: Это определенно верно, когда массив фактически является чем-то, возвращаемым HTMLDOM, однако мне интересно, насколько большим должен быть стандартный массив javascript, чтобы вы могли увидеть заметную разницу? Лично я хотел бы сохранить код как можно более простым, пока не будет доказано, что необходимо сделать что-то другое.
В какой-то момент я начал использовать for (var i = 0, l = a.length; i < a; i++) {} и застрял на нем. Это немного сложнее, чем просто i < a.length, и его влияние на производительность в большинстве случаев не стоит упоминать, но с каждым разом становится легче печатать, и, по крайней мере, я считаю его «более чистым» методом, потому что a.length не обязательно оцениваться каждый раз. Я могу сказать, что доволен своим переходом с MooTools a.each (...).
@Pichan Я думаю, вы имеете в виду i < l, а не i < a, в вашем условии цикла.
@MaxNanasy Да, так что "легче печатать каждый раз": D
for (var i = 0; i <a.length; i ++) метод нельзя использовать для разреженного массива, например, когда вы это делаете; myArray [110] = "фу"; myArray [180] = "тьфу";
Вы должны получить длину массива перед циклом for, потому что в противном случае .length будет оцениваться на каждой итерации цикла.
Только что обнаружил интересный баг. for ... in statement преобразует целое число в строку при итерации массива целых чисел.
Ты узнаешь что-то новое каждый день...
есть различия в производительности в зависимости от того, какой цикл вы используете и в каком браузере.
Например:
for (var i = myArray.length-1; i >= 0; i--)
в некоторых браузерах почти в два раза быстрее, чем:
for (var i = 0; i < myArray.length; i++)
Однако, если ваши массивы ОГРОМНЫ или вы их постоянно зацикливаете, все они достаточно быстры. Я серьезно сомневаюсь, что зацикливание массива является узким местом в вашем проекте (или для любого другого проекта в этом отношении)
Может ли сохранение "myArray.length" в переменной перед циклом исчезнуть из-за разницы в производительности? Думаю, да.
По крайней мере, большая его часть исчезнет. Однако, если я правильно помню, i-- немного быстрее, чем i ++. В любом случае отличия очень небольшие.
Но мы все еще говорим здесь о JavaScript, а не об ассемблере, не так ли? :-)
Я больше не уверен: D Я знаю, что был, но не знаю о тебе
'.' по моему опыту ужасающе низкая производительность в JS (или, по крайней мере, в IE7 JS); для этого стоит уменьшить силу, как здесь.
Нет. MyArray.length - это свойство, а не метод объекта - для определения его значения не производится никаких вычислений. Сохранение его значения в переменной вообще ничего не даст.
Да, есть. Свойства не являются переменными; у них есть код получения / установки.
Я предпочитаю использовать for(var i = myArray.length; i--;)
Самый быстрый способ - использовать цикл while: var l = myArray.length; while (i--) { }
@RenaatDeMuynck, хороший ответ, но вы назначаете длину l и уменьшаете i - нужно выбрать один. Это также эквивалент for(var i = myArray.length - 1; i--; ) { }.
Я тестировал это для (var i = 0, n = myArray.length; i <n; i + = 1) быстрее, чем при использовании i ++ (в Firefox).
Исторически королем был реверс: var L=arr.length; while(L--){}. Затем (примерно в эпоху FF 3.5) for был лучше оптимизирован (вероятно, потому, что он использовался больше всего, особенно с помощью кода «скрипт-кидди»). В то время королем был обратный for: for(var L=arr.length; L--;){}. Затем в последнее время оптимизации стали намного умнее, и мы, наконец, смогли начать замечать промахи в кэше ЦП. Разделение на части (например) массивов int требуется для каждого меньшего / обратного индекса, в то время как при продвижении вперед используется кеш, как задумано. Поскольку for все еще быстрее, теперь обычная петля переадресации часто становится королем.
Код для ясности и удобочитаемости. Не для того, что в некоторых браузерах происходит немного быстрее при использовании абсурдно массивных массивов в текущий момент времени. Оптимизация может измениться, но читабельность вашего кода (или ее отсутствие) не изменится. Напишите код, которому другие могут легко следовать, и позвольте оптимизаторам наверстать упущенное в свое время.
Ибо циклы в массивах несовместимы с прототипом. Если вы думаете, что вам может понадобиться использовать эту библиотеку в будущем, имеет смысл придерживаться циклов for.
http://www.prototypejs.org/api/array
Забудьте «вам может понадобиться использовать эту библиотеку». Вместо этого подумайте: «ваш JS может быть включен в что-нибудь еще, который использует эту библиотеку», потому что проблемы все еще возникают.
Я придерживаюсь второго мнения, что вы должны выбрать метод итерации в соответствии с вашими потребностями. Я бы посоветовал вам на самом деле не выполнять цикл Когда-либо через собственный Array со структурой for in. Это намного медленнее и, как сейчас указал Чейз Зайберт, несовместимо с фреймворком Prototype.
Есть отличный эталонный тест различных стилей циклов, на которые вам обязательно стоит обратить внимание, если вы работаете с JavaScript.. Не делайте ранней оптимизации, но вы должны держать это где-нибудь в затылке.
Я бы использовал for in для получения всех свойств объекта, что особенно полезно при отладке ваших скриптов. Например, мне нравится иметь эту строку под рукой, когда я исследую незнакомый объект:
l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.info(l);
Он выгружает содержимое всего объекта (вместе с телами методов) в мой журнал Firebug. Очень удобно.
Ссылка сейчас не работает. Обязательно хотел бы бенчмарк посмотреть, если у кого-то есть еще ссылка.
Циклы Foreach ломаются в прототипе? Поскольку сейчас это широко поддерживается, прототип должен решить эту проблему.
Ссылка в ответе устарела - «404 | Не найдено | Контента, который вы ищете, больше нет на этой странице».
Дуглас Крокфорд в JavaScript: хорошие стороны (стр. 24) рекомендует избегать использования оператора for in.
Если вы используете for in для перебора имен свойств в объекте, результаты не упорядочиваются. Хуже того: вы можете получить неожиданные результаты; он включает элементы, унаследованные от цепочки прототипов, и имена методов.
Все, кроме свойств, можно отфильтровать с помощью .hasOwnProperty. Этот пример кода делает то, что вы, вероятно, изначально хотели:
for (var name in obj) {
if (Object.prototype.hasOwnProperty.call(obj, name)) {
// DO STUFF
}
}
Во-вторых, foreach ... не делает то, что вы могли ожидать (особенно, если исходил из .net), и, вероятно, это не то, что вы пытаетесь сделать.
for ... in идеально подходит для перебора свойств объекта. Это не подходит для перебора элементов массива. Если вы не можете понять разницу между этими сценариями, то да, вам следует избегать for ... in; в противном случае сходите с ума.
Хочу подчеркнуть то, что НЕ ЗАКАЗАНО! Это может быть большой проблемой, и ее будет сложно отловить.
Теоретически это не упорядочено, но во всех современных браузерах так и есть (за исключением некоторых странных случаев в Chrome, по-видимому). Я считаю, что в будущих сценариях JavaScripts будет гарантированный порядок.
+1 за «включает элементы, унаследованные от цепочки прототипов, и названия методов». Вы получите удовольствие, если, например, кто-то использует ваш код с загруженным прототипом (даже если ваш код на самом деле его не использует).
Не забудьте указать в объявлять переменную name: for(var name in object)..., в противном случае, если этот код находится внутри функции, например, переменная name в конечном итоге станет свойством глобального объекта (это делает присваивание необъявленный идентификатор), также в новый ECMAScript 5 Strict Mode, этот код выдаст ReferenceError.
Также будьте осторожны, если вы перебираете класс, вы получите его дочерние элементы, включая все его пользовательские прототипы и т. д. Это не очень эффективно, даже если вы используете hasOwnProperty () и другой сценарий, чтобы придерживаться простого оператора for. Как уже упоминалось, подходите к операторам in только тогда, когда у вас есть ассоциативные массивы
@Nosredna: есть проблема с порядком итераций для Chrome, поданная никем иным, как Джоном Ресигом, которая помечена как WontFix. code.google.com/p/chromium/issues/detail?id=883. Даже до chrome порядок итераций в браузерах не был одинаковым, если вы удалили, а затем снова добавили свойство. Также IE 9 во многом похож на хром (предположительно для улучшения скорости). Итак ... Пожалуйста, прекратите распространять неточную информацию, было бы очень наивно полагаться на нее.
@Nosredna: См. Раздел «Примечания» под msdn.microsoft.com/en-us/library/55wb2d34(v=vs.94).aspx.
Я видел проблемы с "для каждого" с использованием объектов, прототипов и массивов.
я понимаю, что for each предназначен для свойств объектов, а НЕ массивов
вот что я сделал.
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';
}
});
Я только что сделал это, поэтому я открыт для предложений :)
Отличный материал - довольно просто!
Метод each(callback) jQuery по умолчанию использует цикл for( ; ; ) и будет использовать for( in )Только, если длина равна undefined.
Поэтому я бы сказал, что при использовании этой функции можно с уверенностью принять правильный порядок.
Пример:
$(['a','b','c']).each(function() {
alert(this);
});
//Outputs "a" then "b" then "c"
Обратной стороной использования этого является то, что если вы выполняете некоторую логику, отличную от пользовательского интерфейса, ваши функции будут менее переносимы для других фреймворков. Функцию each(), вероятно, лучше всего зарезервировать для использования с селекторами jQuery, в противном случае рекомендуется использовать for( ; ; ).
Всегда есть documentcloud.github.com/underscore, в котором есть _.each и множество других полезных функций.
Означает ли это также, что если у меня есть свойство length в моем объекте, $ .each завершится ошибкой? например x = {a: "1", b: "2", длина: 3}.
Если вы действительно хотите ускорить свой код, что насчет этого?
for( var i=0,j=null; j=array[i++]; foo(j) );
это своего рода логика while внутри оператора for, и она менее избыточна. Также у firefox есть Array.forEach и Array.filter
Почему это ускорит ваш код? Я не понимаю, почему запись подобных заявлений может ускорить его.
Обратите внимание, что собственный метод Array.forEach теперь широко поддерживаемый.
Что оно делает? Есть ли у него проблемы, упомянутые в других сообщениях (перебор свойств вместо элементов массива)? Кроме того, поскольку IE8 не поддерживает его, будет немного преувеличением сказать, что он широко поддерживается.
Как бы ни презирали его пользователи * nix, IE8 - это больше всего пользователи sub-windows7. это огромная часть рынка браузеров.
@Rauni - Я понимаю вашу точку зрения, но для настольных устройств доля браузера IE составляет менее 40%, согласно en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_ta ble, а согласно markethare.hitslink.com/… и другим сайтам, по крайней мере, 8% браузеров - это IE 9. Другими словами, Array. forEach поддерживается примерно 70% настольных браузеров, поэтому я не думаю, что «широко поддерживается» неразумно. Я не проверял, но мобильная поддержка (в браузерах WebKit и Opera) может быть даже выше. Очевидно, существуют значительные географические различия.
Спасибо за обновление. Я согласен с тем, что можно было бы сказать, что он «широко поддерживается». Единственная проблема в том, что если пользователь использует этот метод JS, он / она все равно должен написать метод резервного копирования на случай, если он не поддерживается.
@Rauni - вы можете использовать es5-shim и es6-shim для автоматического предоставления методов резервного копирования. github.com/es-shims/es5-shim
@MichielvanderBlonk - Самая щедрая оценка для людей, все еще использующих IE 7 или старше, которую я могу найти в 2015 году, составляет 15% от NetApplications. Большинство других источников указывают это как 3% или меньше - достаточно тривиально, чтобы их можно было игнорировать.
Эти два не совпадают, когда массив разреженный.
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.
}
Обновленный ответ за 2012 год Текущая версия всех основных браузеров - Chrome, Firefox, IE9, Safari и Opera поддерживают собственный массив ES5. ForEach.
Если у вас нет причин для поддержки IE8 изначально (помня, что этим пользователям может быть предоставлена прокладка ES5 или рамка Chrome, что обеспечит правильную среду JS), будет проще просто использовать правильный синтаксис языка:
myArray.forEach(function(item, index) {
console.info(item, index);
});
Полная документация для array.forEach () находится на MDN.
Вам действительно следует задокументировать параметры обратного вызова: 1-е значение элемента, 2-е значение индекса элемента, 3-е значение массива
Я слышу, что вы говорите, но в данном случае чрезмерное упрощение скрывает весь диапазон возможностей. Наличие и индекса, и значения означает, что он может служить заменой как для ... in, так и для каждого ... in - с бонусом, заключающимся в том, что вам не нужно запоминать, какие итерации проходят по ключам или значениям.
В корпоративной среде есть масса людей, которые все еще придерживаются IE 6, 7 и 8. Я определенно позабочусь о том, чтобы ваш выбор реализации соответствовал вашей аудитории.
@Cory: ES5 forEach можно довольно легко добавить в устаревшие браузеры ES3. Чем меньше кода, тем лучше.
@nailer Можно ли использовать это как взаимозаменяемые для массивов и объектов?
@hitautodestruct Это часть прототипа массива, а не объекта. Обычно в сообществе сейчас объекты, не являющиеся массивами, по-прежнему повторяются с помощью 'for (var key in object) {}'.
Использование 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.info(tst.hasOwnProperty(i) + i + tst[i]);
}
Object.keys(tst).forEach(function (val) {
console.info(val + tst[val]);
});
блин, это подло. Чтобы добраться до этого, стоило прочитать еще 50 постов. obj = {"розовый": "утки", красный: "гуси"}; Object.keys (obj) === ["розовый", "красный"]
Используйте цикл Array (). ForEach, чтобы воспользоваться преимуществами параллелизма
JavaScript в браузере является параллельным циклом обработки событий, поэтому Array.prototype.forEach не будет выполнять несколько вызовов обратного вызова параллельно.
Осторожно!
Если у вас есть несколько тегов сценария и вы ищете информацию, например, в атрибутах тегов, вам необходимо использовать свойство .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
Будь осторожен!!! Я использую 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
Более короткий и лучший код согласно jsperf -
keys = Object.keys(obj);
for (var i = keys.length; i--;){
value = obj[keys[i]];// or other action
}
для(;;) для Массивы: [20,55,33]
для..в для Объекты: {x: 20, y: 55: z: 33}
Между ними есть важное различие. For-in выполняет итерацию по свойствам объекта, поэтому, когда case является массивом, он будет перебирать не только его элементы, но также и функцию «удалить», которую он имеет.
for (var i = 0; i < myArray.length; i++) {
console.info(i)
}
//Output
0
1
for (var i in myArray) {
console.info(i)
}
// Output
0
1
remove
Вы можете использовать фор-ин с if (myArray.hasOwnProperty(i)). Тем не менее, при переборе массивов я всегда предпочитаю избегать этого и просто использовать оператор for (;;).
Хотя они оба очень похожи, есть небольшая разница:
var array = ["a", "b", "c"];
array["abc"] = 123;
console.info("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
console.info(" array[" + index + "] = " + array[index]); //Standard for loop
}
в этом случае вывод:
СТАНДАРТ ДЛЯ ПЕТЛИ:
ARRAY [0] = A
ARRAY [1] = B
ARRAY [2] = C
console.info("For-in loop:");
for (var key in array)
{
console.info(" array[" + key + "] = " + array[key]); //For-in loop output
}
в то время как в этом случае вывод:
FOR-IN LOOP:
ARRAY [1] = B
ARRAY [2] = C
ARRAY [10] = D
ARRAY [ABC] = 123
Обратите внимание, что у нас также, начиная с JS 1.6, есть:
myArray.forEach(callback[, thisarg]).