Я видел недавний вопрос, где кто-то сказал вам это, но они предназначались только для массивов. Это считается плохой практикой для итерации по массивам, но не обязательно для итерации по элементам объекта.
ммурч
19
Множество ответов с помощью циклов for, таких как for (var i = 0; i <hColl.length; i ++) {}, сравнивают с var i = hColl.length; тогда как (i--) {} ', который, когда возможно использовать последнюю форму, значительно быстрее. Я знаю, что это тангенциально, но думал, что добавлю это немного.
Марк Шультхайс
2
@MarkSchultheiss, но это обратная итерация. Есть ли другая версия прямой итерации, которая быстрее?
ma11hew28
5
@Wynand использовать var i = hCol1.length; for (i;i;i--;) {}кеш i, как это будет иметь значение, и упростить тест. - чем старше браузер, тем больше различие между forи whileВСЕГДА кеширующим счетчиком «i» - и, конечно, отрицательное не всегда соответствует ситуации, а отрицательное в то время obfuscate как код немного для некоторых людей. и обратите внимание, var i = 1000; for (i; i; i--) {}и var b =1000 for (b; b--;) {}где я перехожу от 1000 до 1, а b - от 999 до 0. - чем старше браузер, тем больше в то же время склоняется к производительности.
Марк Шультхайс,
9
Вы также можете быть умным. for(var i = 0, l = myArray.length; i < l; ++i) ...это самый быстрый и лучший способ получить итерацию вперед.
Матье Амиот
Ответы:
1557
Причина в том, что одна конструкция:
var a =[];// Create a new empty array.
a[5]=5;// Perfectly legal JavaScript that resizes the array.for(var i =0; i < a.length; i++){// Iterate over numeric indexes from 0 to 5, as everyone expects.
console.log(a[i]);}/* Will display:
undefined
undefined
undefined
undefined
undefined
5
*/
Также учтите, что библиотеки JavaScript могут делать такие вещи, которые влияют на любой создаваемый вами массив:
// Somewhere deep in your JavaScript library...Array.prototype.foo =1;// Now you have no idea what the below code will do.var a =[1,2,3,4,5];for(var x in a){// Now foo is a part of EVERY array and // will show up here as a value of 'x'.
console.log(x);}/* Will display:
0
1
2
3
4
foo
*/
Исторически, некоторые браузеры даже итерировали по «длине», «toString» и т. Д.!
bobince
398
Не забудьте использовать, (var x in a)а не (x in a)- не хотите создавать глобальный.
Крис Морган
78
Первая проблема - не причина, по которой она плохая, а только разница в семантике. Вторая проблема кажется мне причиной (помимо столкновений между библиотеками, делающими то же самое), что изменение прототипа встроенного типа данных - это плохо, а не то, что ... в это плохо.
Стюарт
86
@Stewart: все объекты в JS являются ассоциативными. JS Array - это объект, так что да, он тоже ассоциативный, но не для этого. Если вы хотите перебрать ключи объекта , используйте for (var key in object). Однако, если вы хотите перебрать элементы массива , используйте for(var i = 0; i < array.length; i += 1).
Мартейн
42
В первом примере вы сказали, что он перебирает числовые индексы от 0 до 4, как и все ожидают , я ожидаю , что он будет перебирать от 0 до 5 ! Поскольку, если вы добавите элемент в позицию 5, массив будет иметь 6 элементов (5 из них не определены).
stivlo
393
Само for-inпо себе утверждение не является «плохой практикой», однако его можно неправильно использовать , например, для итерации. массивов или объектов, подобных массивам.
Цель этого for-inутверждения - перечислить свойства объекта. Это утверждение будет идти вверх по цепочке прототипов, также перечисляя наследуемые свойства, что иногда нежелательно.
Кроме того, порядок итерации не гарантируется спецификацией, что означает, что если вы хотите «перебрать» объект массива, с помощью этого оператора вы не можете быть уверены, что свойства (индексы массива) будут посещаться в числовом порядке.
Например, в JScript (IE <= 8) порядок перечисления даже для объектов Array определяется по мере создания свойств:
var array =[];
array[2]='c';
array[1]='b';
array[0]='a';for(var p in array){//... p will be "2", "1" and "0" on IE}
Кроме того, если говорить о унаследованных свойствах, если, например, вы расширяете Array.prototypeобъект (как это делают некоторые библиотеки, как это делает MooTools), эти свойства также будут перечислены:
Array.prototype.last =function(){returnthis[this.length-1];};for(var p in []){// an empty array// last will be enumerated}
Как я уже говорил, для итерации по массивам или объектам, похожим на массивы, лучше всего использовать последовательный цикл , такой как обычный старый for/ whileцикл.
Если вы хотите перечислить только собственные свойства объекта (те, которые не наследуются), вы можете использовать hasOwnPropertyметод:
for(var prop in obj){if(obj.hasOwnProperty(prop)){// prop is not inherited}}
А некоторые люди даже рекомендуют вызывать метод напрямую, Object.prototypeчтобы избежать проблем, если кто-то добавит свойство с именем hasOwnPropertyк нашему объекту:
for(var prop in obj){if(Object.prototype.hasOwnProperty.call(obj, prop)){// prop is not inherited}}
Вопрос о последнем пункте о hasOwnProperty: если кто-то переопределит hasOwnProperty объекта, у вас будут проблемы. Но не будет ли у вас таких же проблем, если кто-то переопределит «Object.prototype.hasOwnProperty»? В любом случае, они облажались, и это не ваша ответственность, верно?
Скотт Риппи
Вы говорите, что for..inэто не плохая практика, но ею можно злоупотреблять. У вас есть реальный пример хорошей практики, где вы действительно хотите просмотреть все свойства объектов, включая унаследованные свойства?
с помощью этого ответа я обнаружил, что могу получить доступ к значению с помощьюfor (var p in array) { array[p]; }
equiman
117
Есть три причины, почему вы не должны использовать for..inдля перебора элементов массива:
for..inбудет зацикливаться на всех собственных и унаследованных свойствах объекта массива, которые не являются DontEnum; это означает, что если кто-то добавляет свойства к конкретному объекту массива (для этого есть веские причины - я сам это сделал) или изменяю Array.prototype(что считается плохой практикой в коде, который должен хорошо работать с другими сценариями), эти свойства будут итерироваться; Унаследованные свойства могут быть исключены путем проверки hasOwnProperty(), но это не поможет вам со свойствами, установленными в самом объекте массива.
for..in не гарантируется сохранение порядка элементов
это медленно, потому что вы должны пройти все свойства объекта массива и всей цепочки его прототипов и все равно получите только имя свойства, т.е. чтобы получить значение, потребуется дополнительный поиск
Потому что for ... in перечисляет объект, который содержит массив, а не сам массив. Если я добавлю функцию в цепочку прототипов массивов, она также будет включена. Т.е.
Array.prototype.myOwnFunction =function(){ alert(this);}
a =newArray();
a[0]='foo';
a[1]='bar';for(x in a){
document.write(x +' = '+ a[x]);}
Это напишет:
0 = foo
1 = бар
myOwnFunction = function () {alert (this); }
И поскольку вы никогда не можете быть уверены, что ничего не будет добавлено в цепочку прототипов, просто используйте цикл for для перечисления массива:
Массивы являются объектами, нет «объекта, который содержит массив».
RobG
41
В изоляции нет ничего плохого в использовании for-in для массивов. For-in перебирает имена свойств объекта, а в случае массива «из коробки» свойства соответствуют индексам массива. (Встроенный в propertes , как length, toStringи так далее, не включаются в итерации.)
Однако если ваш код (или используемая вами среда) добавляет пользовательские свойства к массивам или к прототипу массива, то эти свойства будут включены в итерацию, что, вероятно, не то, что вам нужно.
Некоторые JS-фреймворки, такие как Prototype, модифицируют прототип Array. Другие фреймворки, такие как JQuery, нет, поэтому с JQuery вы можете безопасно использовать for-in.
Если у вас есть сомнения, вы, вероятно, не должны использовать for-in.
Альтернативный способ перебора массива заключается в использовании цикла for:
for(var ix=0;ix<arr.length;ix++) alert(ix);
Однако у этого есть другая проблема. Проблема в том, что массив JavaScript может иметь «дыры». Если вы определите arrкак:
var arr =["hello"];
arr[100]="goodbye";
Тогда у массива есть два элемента, но длина 101. Использование for-in даст два индекса, тогда как цикл for даст 101 индекс, где значение 99 имеет значение undefined.
Более длинный ответ: это просто не стоит того, даже если последовательный порядок элементов и оптимальная производительность не требуются.
Длинный ответ: это просто не стоит ...
Использование for (var property in array)приведет arrayк тому, что оно будет перебираться как объект , проходя цепочку прототипов объекта и, в конечном итоге, работая медленнее, чем forцикл на основе индекса .
for (... in ...) не гарантируется возвращение свойств объекта в последовательном порядке, как можно было бы ожидать.
Использование hasOwnProperty()и !isNaN()проверки для фильтрации свойств объекта являются дополнительными издержками, заставляющими его работать еще медленнее, и сводят на нет основную причину его использования в первую очередь, то есть из-за более сжатого формата.
По этим причинам приемлемый компромисс между производительностью и удобством даже не существует. На самом деле нет никакой пользы, если только мы не хотим обрабатывать массив как объект и выполнять операции со свойствами объекта массива.
В дополнение к причинам, приведенным в других ответах, вы, возможно, не захотите использовать структуру «for ... in», если вам нужно выполнить математику с переменной counter, поскольку цикл повторяет имена свойств объекта и, следовательно, переменной это строка
Вы можете использовать префикс +вместо, parseIntесли вам действительно не нужно целое число или игнорировать недопустимые символы.
Конрад Боровски
Кроме того, использование parseInt()не рекомендуется. Попробуйте, parseInt("025");и это не удастся.
Дерек 朕 會 功夫
6
@Derek 朕 會 功夫 - вы определенно можете использовать parseInt. Проблема в том, что если вы не включите основание, старые браузеры могут попытаться интерпретировать число (таким образом, 025 становится восьмеричным). Это было исправлено в ECMAScript 5, но это все еще происходит для чисел, начинающихся с «0x» (он интерпретирует число как шестнадцатеричное). Чтобы быть в безопасности, используйте основание, чтобы указать число, например, так parseInt("025", 10): оно определяет базу 10.
Механика и порядок перечисления свойств ... не уточняется ...
(Акцент мой.)
Это означает, что если браузер хочет, он может просматривать свойства в том порядке, в котором они были вставлены. Или в порядке номеров. Или в лексическом порядке (где «30» предшествует «4»! Помните, что все ключи объектов - и, следовательно, все индексы массивов - на самом деле являются строками, так что это имеет смысл). Он мог бы проходить через них, если реализовывал объекты в виде хеш-таблиц. Или возьмите что-нибудь из этого и добавьте «назад». Браузер может даже выполнять итерацию случайным образом и быть совместимым с ECMA-262, если он посещает каждое свойство ровно один раз.
На практике большинство браузеров в настоящее время любят выполнять итерации примерно в одном и том же порядке. Но ничто не говорит, что они должны. Это зависит от реализации и может измениться в любое время, если будет найден другой способ, который будет гораздо более эффективным.
В любом случае for... не inнесет в себе никакого смысла порядка. Если вы заботитесь о порядке, проясните это и используйте обычный forцикл с индексом.
Как уже говорили другие, вы можете получить ключи, которых нет в вашем массиве или которые унаследованы от прототипа. Итак, если, скажем, библиотека добавляет свойство к прототипам Array или Object:
Array.prototype.someProperty =true
Вы получите его как часть каждого массива:
for(var item in [1,2,3]){
console.log(item)// will log 1,2,3 but also "someProperty"}
Вы можете решить это с помощью метода hasOwnProperty:
var ary =[1,2,3];for(var item in ary){if(ary.hasOwnProperty(item)){
console.log(item)// will log only 1,2,3}}
но это верно для итерации любого объекта с циклом for-in.
Два
Обычно порядок элементов в массиве важен, но цикл for-in не обязательно повторяется в правильном порядке, потому что он обрабатывает массив как объект, как это реализовано в JS, а не как массив. Это кажется небольшой вещью, но она действительно может испортить приложения и ее трудно отладить.
Object.keys(a).forEach( function(item) { console.log(item) } )переберите массив собственных ключей свойств, а не тех, которые унаследованы от прототипа.
Qwerty
2
Правда, но, как и в цикле for-in, он не обязательно будет в правильном порядке индекса. Кроме того, он не будет работать на старых браузерах, не поддерживающих ES5.
В Firefox 3 вы также можете использовать arr.forEach или for (var [i, v] в Iterator (arr)) {}, но ни одна из этих функций не работает в IE, хотя вы можете написать метод forEach самостоятельно.
Вава
и практически у каждой библиотеки есть свой метод для этого тоже.
Вава
5
Этот ответ неверен. длина не будет включена в итерацию for-in. Это только свойства, которые вы добавляете сами, которые включены.
JacquesB
16
Я не думаю, что мне есть что добавить, например. Ответ Триптиха или ответ CMS о том, почему for...inв некоторых случаях следует избегать использования.
Однако я хотел бы добавить, что в современных браузерах есть альтернатива, for...inкоторую можно использовать в тех случаях, когда for...inее нельзя использовать. Эта альтернатива for...of:
for(var item of items){
console.log(item);}
Замечания :
К сожалению, ни одна версия Internet Explorer не поддерживает for...of( Edge 12+ ), поэтому вам придется подождать немного дольше, пока вы не сможете использовать ее в своем рабочем коде на стороне клиента. Тем не менее, он должен быть безопасным для использования в вашем JS-коде на стороне сервера (если вы используете Node.js ).
@georgeawg Вы имели в виду for-of, не так for-inли?
ᆼ ᆺ ᆼ
15
Проблема с for ... in ...- и это становится проблемой только тогда, когда программист действительно не понимает язык; на самом деле это не ошибка или что-то в этом роде - это то, что она перебирает все элементы объекта (ну, все перечисляемые элементы, но пока это подробно). Когда вы хотите выполнить итерации только по индексированным свойствам массива, единственный гарантированный способ сохранить семантически непротиворечивые вещи - это использовать целочисленный индекс (то есть for (var i = 0; i < array.length; ++i)цикл стиля).
Любой объект может иметь произвольные свойства, связанные с ним. В частности, не было бы ничего страшного в загрузке дополнительных свойств в экземпляр массива. Код, который хочет видеть только индексированные свойства массива, должен придерживаться целочисленного индекса. Код, который полностью осведомлен о том, что for ... inделает и действительно должен видеть все свойства, ну тогда это тоже нормально.
Хорошее объяснение Пойнти. Просто любопытно. Если бы у меня был массив, который был внутри объекта со свойствами умножения и имел бы for in, по сравнению с обычным циклом for, эти массивы были бы перебраны? (что по сути будет медленной производительностью, верно?)
NiCk Newman
2
@NiCkNewman также объект вы ссылаетесь после inв for ... inцикле будет просто
Заостренный
Понимаю. Просто любопытно, потому что у меня есть объекты и массивы внутри моего основного игрового объекта, и мне было интересно, будет ли это более болезненным для иннинга, чем просто обычный цикл for для индексов.
NiCk Ньюман
@NiCkNewman хорошо, тема всего этого вопроса в том, что вы просто не должны использовать for ... inмассивы; Есть много веских причин не делать этого. Это не столько проблема производительности, сколько проблема «убедитесь, что она не сломалась».
Заостренный
Ну, технически мои объекты хранятся в массиве, поэтому я волновался, что-то вроде:, [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]но да, я понимаю.
NiCk Ньюман
9
Кроме того, из-за семантики способ обработки for, inмассивов (т. Е. Так же, как и любого другого объекта JavaScript) не согласован с другими популярными языками.
// C#char[] a =newchar[]{'A','B','C'};
foreach (char x in a)System.Console.Write(x);//Output: "ABC"// Javachar[] a ={'A','B','C'};for(char x : a)System.out.print(x);//Output: "ABC"// PHP
$a = array('A','B','C');
foreach ($a as $x) echo $x;//Output: "ABC"// JavaScriptvar a =['A','B','C'];for(var x in a) document.write(x);//Output: "012"
TL & DR: использованиеfor in цикла в массивах не является злом, на самом деле это совсем наоборот.
Я думаю, что for inцикл является жемчужиной JS, если он используется правильно в массивах. От вас ожидают, что вы полностью контролируете свое программное обеспечение и знаете, что делаете. Давайте посмотрим на упомянутые недостатки и опровергаем их один за другим.
Он также проходит через унаследованные свойства: прежде всего любые расширения для Array.prototypeдолжны были быть сделаны с помощью Object.defineProperty()и их enumerableдескриптор должен быть установлен в false. Любая библиотека, не делающая этого, не должна использоваться вообще.
Свойства, которые вы добавляете в цепочку наследования, впоследствии учитываются: при выполнении подклассов массива Object.setPrototypeOfпо классу или по классу extend. Вы должны снова использовать Object.defineProperty()которые наборами по умолчанию в writable, enumerableи configurableдескрипторов свойств для false. Давайте посмотрим пример подклассификации массива здесь ...
functionStack(...a){var stack =newArray(...a);Object.setPrototypeOf(stack,Stack.prototype);return stack;}Stack.prototype =Object.create(Array.prototype);// now stack has full access to array methods.Object.defineProperty(Stack.prototype,"constructor",{value:Stack});// now Stack is a proper constructorObject.defineProperty(Stack.prototype,"peak",{value:function(){// add Stack "only" methods to the Stack.prototype.returnthis[this.length-1];}});var s =newStack(1,2,3,4,1);
console.log(s.peak());
s[s.length]=7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);for(var i in s) console.log(s[i]);
Итак, вы видите ... for inцикл теперь безопасен, так как вы заботились о своем коде.
Не for inпетля медленно: Ад нет. Это самый быстрый способ итерации, если вы зацикливаетесь на разреженных массивах, которые время от времени необходимы. Это один из самых важных приемов производительности, которые нужно знать. Давайте посмотрим на пример. Мы будем зацикливаться на разреженном массиве.
var a =[];
a[0]="zero";
a[10000000]="ten million";
console.time("for loop on array a:");for(var i=0; i < a.length; i++) a[i]&& console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");for(var i in a) a[i]&& console.log(a[i]);
console.timeEnd("for in loop on array a:");
@Ravi Shanker Reddy Хороший бенчмаркинг настроен. Как я уже упоминал в своем ответе, for inцикл затмевает другие, «если» массив редок, и чем больше, тем больше его размер. Поэтому я переставил стендовый тест для разреженного массива, arrразмером ~ 10000, только с 50 предметами, случайно выбранными из [42,"test",{t:1},null, void 0]случайных индексов. Вы сразу заметите разницу. - >> Проверьте это здесь << - .
Redu
8
В дополнение к другим проблемам, синтаксис for..in, вероятно, медленнее, поскольку индекс представляет собой строку, а не целое число.
var a =["a"]for(var i in a)
alert(typeof i)// 'string'for(var i =0; i < a.length; i++)
alert(typeof i)// 'number'
Вероятно, не имеет большого значения. Элементы массива - это свойства объекта на основе массива или объекта, подобного массиву, и все свойства объекта имеют строковые ключи. Если ваш движок JS каким-то образом не оптимизирует его, даже если вы используете число, оно в конечном итоге превратится в строку для поиска.
Цао
Независимо от каких-либо проблем с производительностью, если вы новичок в JavaScript, используйте var i in aи ожидаете, что индекс будет целочисленным, то выполнение чего-то подобного a[i+offset] = <value>будет помещать значения в совершенно неправильные места. («1» + 1 == «11»).
szmoore
8
Важным аспектом является то, что for...inвыполняется только перебор свойств, содержащихся в объекте, для атрибута перечисляемого свойства которого установлено значение true. Таким образом, если кто-то пытается перебрать объект, используя, for...inтогда могут быть пропущены произвольные свойства, если их атрибут перечислимого свойства равен false. Вполне возможно изменить атрибут перечислимого свойства для обычных объектов Array, чтобы определенные элементы не перечислялись. Хотя в целом атрибуты свойств обычно применяются к свойствам функций внутри объекта.
Можно проверить значение атрибута перечисляемого свойства свойств:
Эта функция доступна в ECMAScript 5 - в более ранних версиях было невозможно изменить значение атрибута перечисляемого свойства (для него всегда было установлено значение true).
for/ inРаботает с двумя типами переменных: HashTables (ассоциативные массивы) и массив (не ассоциативной).
JavaScript автоматически определит способ прохождения элементов. Поэтому, если вы знаете, что ваш массив действительно неассоциативен, вы можете использовать его for (var i=0; i<=arrayLen; i++)и пропустить итерацию автоопределения.
Но, по моему мнению, лучше использовать for/ in, процесс, необходимый для этого автоопределения, очень мал.
Реальный ответ на этот вопрос будет зависеть от того, как браузер анализирует / интерпретирует код JavaScript. Это может измениться между браузерами.
Я не могу думать о других целях, чтобы не использовать for/ in;
//Non-associativevar arr =['a','b','c'];for(var i in arr)
alert(arr[i]);//Associativevar arr ={
item1 :'a',
item2 :'b',
item3 :'c'};for(var i in arr)
alert(arr[i]);
Недостаточно - вполне нормально добавлять произвольные именованные свойства в экземпляры массива, и они будут проверяться trueпри hasOwnProperty()проверках.
Заостренный
Хороший вопрос, спасибо. Я никогда не был настолько глуп, чтобы делать это самому массиву, поэтому я не учел это!
JAL
1
@Pointy Я не проверял это, но, возможно, это можно преодолеть с помощью isNaNпроверки каждого имени свойства.
WynandB
1
@ Вин и интересная идея; однако я не понимаю, почему это стоит того, чтобы итерация с простым числовым индексом была такой простой.
Заостренный
@WynandB извините за удар, но я чувствую, что исправление в порядке: isNaNдля проверки, является ли переменная специальным значением NaN или нет, она не может использоваться для проверки «вещей, отличных от чисел» (вы можете пойти с обычным typeof для этого).
Дольдт
6
Это не обязательно плохо (в зависимости от того, что вы делаете), но в случае массивов, если что-то было добавлено Array.prototype, вы получите странные результаты. Где вы ожидаете, что этот цикл будет выполняться три раза:
var arr =['a','b','c'];for(var key in arr){...}
Если функция называется helpfulUtilityMethodдобавлена Array«с prototype, то ваш цикл будет в конечном итоге работает в четыре раза: keyбыло бы 0, 1, 2и helpfulUtilityMethod. Если бы вы ожидали только целые числа, упс.
Просто примечание о SO - «выше» нет, потому что комментарии постоянно меняют порядок на странице. Итак, мы не знаем, какой комментарий вы имеете в виду. По этой причине хорошо сказать «в комментарии х человека».
JAL
@JAL ... или добавьте постоянную ссылку к ответу.
WynandB
5
Использование for...inцикла для массива не является неправильным, хотя я могу догадаться, почему кто-то сказал вам, что:
1.) Уже есть функция или метод более высокого порядка, который имеет эту цель для массива, но имеет больше функциональности и более тонкий синтаксис, называемый «forEach»: Array.prototype.forEach(function(element, index, array) {} );
2.) Массивы всегда имеют длину, но for...inи forEachне выполняют функции для любого значения, которое'undefined' только для индексов , которые имеют значение , определенное. Таким образом, если вы присваиваете только одно значение, эти циклы будут выполнять функцию только один раз, но, поскольку массив перечисляется, он всегда будет иметь длину до самого высокого индекса, который имеет определенное значение, но эта длина может остаться незамеченной при использовании этих значений. петли.
3.) Стандарт цикла for будет выполнять функцию столько раз, сколько вы определяете в параметрах, и, поскольку массив нумеруется, имеет смысл определить, сколько раз вы хотите выполнить функцию. В отличие от других циклов, цикл for может затем выполнять функцию для каждого индекса в массиве, независимо от того, определено это значение или нет.
По сути, вы можете использовать любой цикл, но вы должны точно помнить, как они работают. Понять условия, при которых повторяются различные циклы, их отдельные функции и понять, что они будут более или менее подходящими для различных сценариев.
Кроме того, может считаться лучшей практикой использовать forEachметод, чем for...inцикл в целом, потому что он легче писать и имеет больше функциональных возможностей, поэтому вы можете захотеть использовать только этот метод и стандарт для, но ваш вызов.
Ниже показано, что первые два цикла выполняют операторы console.log только один раз, в то время как стандарт цикла выполняет функцию столько раз, сколько указано, в данном случае, array.length = 6.
var arr =[];
arr[5]='F';for(var index in arr){
console.log(index);
console.log(arr[index]);
console.log(arr)}// 5// 'F'// => (6) [undefined x 5, 6]
arr.forEach(function(element, index, arr){
console.log(index);
console.log(element);
console.log(arr);});// 5// 'F'// => Array (6) [undefined x 5, 6]for(var index =0; index < arr.length; index++){
console.log(index);
console.log(arr[index]);
console.log(arr);};// 0// undefined// => Array (6) [undefined x 5, 6]// 1// undefined// => Array (6) [undefined x 5, 6]// 2// undefined// => Array (6) [undefined x 5, 6]// 3// undefined// => Array (6) [undefined x 5, 6]// 4// undefined// => Array (6) [undefined x 5, 6]// 5// 'F'// => Array (6) [undefined x 5, 6]
Вот причины, по которым это (обычно) плохая практика:
for...inциклы перебирают все свои перечисляемые свойства и перечислимые свойства своих прототипов. Обычно в итерации массива мы хотим перебирать только сам массив. И даже если вы сами ничего не можете добавить в массив, ваши библиотеки или фреймворк могут что-то добавить.
Пример :
Array.prototype.hithere ='hithere';var array =[1,2,3];for(let el in array){// the hithere property will also be iterated over
console.log(el);}
for...inциклы не гарантируют определенный порядок итераций . Хотя этот порядок обычно наблюдается в большинстве современных браузеров, гарантия 100% все еще отсутствует.
for...inЦиклы игнорируют undefinedэлементы массива, то есть элементы массива, которые еще не были назначены.
Пример :
const arr =[];
arr[3]='foo';// resize the array to 4
arr[4]=undefined;// add another element with value undefined to it// iterate over the array, a for loop does show the undefined elementsfor(let i =0; i < arr.length; i++){
console.log(arr[i]);}
console.log('\n');// for in does ignore the undefined elementsfor(let el in arr){
console.log(arr[el]);}
for ... in полезен при работе с объектом в JavaScript, но не для Array, но все же мы не можем сказать, что это неправильный путь, но это не рекомендуется, посмотрите на этот пример ниже, используя цикл for ... in :
let txt ="";const person ={fname:"Alireza", lname:"Dezfoolian", age:35};for(const x in person){
txt += person[x]+" ";}
console.log(txt);//Alireza Dezfoolian 35
Хорошо, теперь сделаем это с Array :
let txt ="";const person =["Alireza","Dezfoolian",35];for(const x in person){
txt += person[x]+" ";}
console.log(txt);//Alireza Dezfoolian 35
Как видите результат тот же ...
Но давайте попробуем что-то, давайте создадим что-то для Array ...
Array.prototype.someoneelse ="someoneelse";
Теперь мы создаем новый массив ();
let txt ="";const arr =newArray();
arr[0]='Alireza';
arr[1]='Dezfoolian';
arr[2]=35;for(x in arr){
txt += arr[x]+" ";}
console.log(txt);//Alireza Dezfoolian 35 someoneelse
Вы видите кого- то другого ... ... В этом случае мы фактически перебираем новый объект Array!
Так что это одна из причин, почему мы должны использовать для ... осторожно, но это не всегда так ...
Поскольку элементы JavaScript сохраняются как стандартные свойства объекта, не рекомендуется выполнять итерацию по массивам JavaScript с использованием циклов for ... in, поскольку в списке будут перечислены обычные элементы и все перечисляемые свойства.
хотя этот вопрос конкретно не рассматривается, я хотел бы добавить, что есть очень веская причина не использовать для ... в с NodeList(как можно было бы получить из querySelectorAllвызова, поскольку он вообще не видит возвращаемые элементы, вместо этого итерация только по свойствам NodeList.
var i = hCol1.length; for (i;i;i--;) {}
кеш i, как это будет иметь значение, и упростить тест. - чем старше браузер, тем больше различие междуfor
иwhile
ВСЕГДА кеширующим счетчиком «i» - и, конечно, отрицательное не всегда соответствует ситуации, а отрицательное в то времяobfuscate
как код немного для некоторых людей. и обратите внимание,var i = 1000; for (i; i; i--) {}
иvar b =1000 for (b; b--;) {}
где я перехожу от 1000 до 1, а b - от 999 до 0. - чем старше браузер, тем больше в то же время склоняется к производительности.for(var i = 0, l = myArray.length; i < l; ++i) ...
это самый быстрый и лучший способ получить итерацию вперед.Ответы:
Причина в том, что одна конструкция:
иногда может полностью отличаться от других:
Также учтите, что библиотеки JavaScript могут делать такие вещи, которые влияют на любой создаваемый вами массив:
источник
(var x in a)
а не(x in a)
- не хотите создавать глобальный.for (var key in object)
. Однако, если вы хотите перебрать элементы массива , используйтеfor(var i = 0; i < array.length; i += 1)
.Само
for-in
по себе утверждение не является «плохой практикой», однако его можно неправильно использовать , например, для итерации. массивов или объектов, подобных массивам.Цель этого
for-in
утверждения - перечислить свойства объекта. Это утверждение будет идти вверх по цепочке прототипов, также перечисляя наследуемые свойства, что иногда нежелательно.Кроме того, порядок итерации не гарантируется спецификацией, что означает, что если вы хотите «перебрать» объект массива, с помощью этого оператора вы не можете быть уверены, что свойства (индексы массива) будут посещаться в числовом порядке.
Например, в JScript (IE <= 8) порядок перечисления даже для объектов Array определяется по мере создания свойств:
Кроме того, если говорить о унаследованных свойствах, если, например, вы расширяете
Array.prototype
объект (как это делают некоторые библиотеки, как это делает MooTools), эти свойства также будут перечислены:Как я уже говорил, для итерации по массивам или объектам, похожим на массивы, лучше всего использовать последовательный цикл , такой как обычный старый
for
/while
цикл.Если вы хотите перечислить только собственные свойства объекта (те, которые не наследуются), вы можете использовать
hasOwnProperty
метод:А некоторые люди даже рекомендуют вызывать метод напрямую,
Object.prototype
чтобы избежать проблем, если кто-то добавит свойство с именемhasOwnProperty
к нашему объекту:источник
for..in
намного медленнее, чем «нормальных» циклов.for..in
это не плохая практика, но ею можно злоупотреблять. У вас есть реальный пример хорошей практики, где вы действительно хотите просмотреть все свойства объектов, включая унаследованные свойства?for (var p in array) { array[p]; }
Есть три причины, почему вы не должны использовать
for..in
для перебора элементов массива:for..in
будет зацикливаться на всех собственных и унаследованных свойствах объекта массива, которые не являютсяDontEnum
; это означает, что если кто-то добавляет свойства к конкретному объекту массива (для этого есть веские причины - я сам это сделал) или изменяюArray.prototype
(что считается плохой практикой в коде, который должен хорошо работать с другими сценариями), эти свойства будут итерироваться; Унаследованные свойства могут быть исключены путем проверкиhasOwnProperty()
, но это не поможет вам со свойствами, установленными в самом объекте массива.for..in
не гарантируется сохранение порядка элементовэто медленно, потому что вы должны пройти все свойства объекта массива и всей цепочки его прототипов и все равно получите только имя свойства, т.е. чтобы получить значение, потребуется дополнительный поиск
источник
Потому что for ... in перечисляет объект, который содержит массив, а не сам массив. Если я добавлю функцию в цепочку прототипов массивов, она также будет включена. Т.е.
Это напишет:
И поскольку вы никогда не можете быть уверены, что ничего не будет добавлено в цепочку прототипов, просто используйте цикл for для перечисления массива:
Это напишет:
источник
В изоляции нет ничего плохого в использовании for-in для массивов. For-in перебирает имена свойств объекта, а в случае массива «из коробки» свойства соответствуют индексам массива. (Встроенный в propertes , как
length
,toString
и так далее, не включаются в итерации.)Однако если ваш код (или используемая вами среда) добавляет пользовательские свойства к массивам или к прототипу массива, то эти свойства будут включены в итерацию, что, вероятно, не то, что вам нужно.
Некоторые JS-фреймворки, такие как Prototype, модифицируют прототип Array. Другие фреймворки, такие как JQuery, нет, поэтому с JQuery вы можете безопасно использовать for-in.
Если у вас есть сомнения, вы, вероятно, не должны использовать for-in.
Альтернативный способ перебора массива заключается в использовании цикла for:
Однако у этого есть другая проблема. Проблема в том, что массив JavaScript может иметь «дыры». Если вы определите
arr
как:Тогда у массива есть два элемента, но длина 101. Использование for-in даст два индекса, тогда как цикл for даст 101 индекс, где значение 99 имеет значение
undefined
.источник
По состоянию на 2016 год (ES6) мы можем использовать
for…of
для итерации массива, как уже заметил Джон Слегерс.Я просто хотел бы добавить этот простой демонстрационный код, чтобы прояснить ситуацию:
Консоль показывает:
Другими словами:
for...of
считает от 0 до 5, а также игнорируетArray.prototype.foo
. Показывает значения массива .for...in
перечисляет только5
, игнорируя неопределенные индексы массива, но добавляяfoo
. Он показывает имена свойств массива .источник
Краткий ответ: это просто не стоит.
Более длинный ответ: это просто не стоит того, даже если последовательный порядок элементов и оптимальная производительность не требуются.
Длинный ответ: это просто не стоит ...
for (var property in array)
приведетarray
к тому, что оно будет перебираться как объект , проходя цепочку прототипов объекта и, в конечном итоге, работая медленнее, чемfor
цикл на основе индекса .for (... in ...)
не гарантируется возвращение свойств объекта в последовательном порядке, как можно было бы ожидать.hasOwnProperty()
и!isNaN()
проверки для фильтрации свойств объекта являются дополнительными издержками, заставляющими его работать еще медленнее, и сводят на нет основную причину его использования в первую очередь, то есть из-за более сжатого формата.По этим причинам приемлемый компромисс между производительностью и удобством даже не существует. На самом деле нет никакой пользы, если только мы не хотим обрабатывать массив как объект и выполнять операции со свойствами объекта массива.
источник
В дополнение к причинам, приведенным в других ответах, вы, возможно, не захотите использовать структуру «for ... in», если вам нужно выполнить математику с переменной counter, поскольку цикл повторяет имена свойств объекта и, следовательно, переменной это строка
Например,
Напишу
в то время как,
Напишу
Конечно, это можно легко преодолеть, включив
в цикле, но первая структура более прямая.
источник
+
вместо,parseInt
если вам действительно не нужно целое число или игнорировать недопустимые символы.parseInt()
не рекомендуется. Попробуйте,parseInt("025");
и это не удастся.parseInt
. Проблема в том, что если вы не включите основание, старые браузеры могут попытаться интерпретировать число (таким образом, 025 становится восьмеричным). Это было исправлено в ECMAScript 5, но это все еще происходит для чисел, начинающихся с «0x» (он интерпретирует число как шестнадцатеричное). Чтобы быть в безопасности, используйте основание, чтобы указать число, например, такparseInt("025", 10)
: оно определяет базу 10.Помимо того факта, что
for
...in
перебирает все перечисляемые свойства (что не то же самое, что "все элементы массива"!), См. Http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , раздел 12.6.4 (5-е издание) или 13.7.5.15 (7-е издание):(Акцент мой.)
Это означает, что если браузер хочет, он может просматривать свойства в том порядке, в котором они были вставлены. Или в порядке номеров. Или в лексическом порядке (где «30» предшествует «4»! Помните, что все ключи объектов - и, следовательно, все индексы массивов - на самом деле являются строками, так что это имеет смысл). Он мог бы проходить через них, если реализовывал объекты в виде хеш-таблиц. Или возьмите что-нибудь из этого и добавьте «назад». Браузер может даже выполнять итерацию случайным образом и быть совместимым с ECMA-262, если он посещает каждое свойство ровно один раз.
На практике большинство браузеров в настоящее время любят выполнять итерации примерно в одном и том же порядке. Но ничто не говорит, что они должны. Это зависит от реализации и может измениться в любое время, если будет найден другой способ, который будет гораздо более эффективным.
В любом случае
for
... неin
несет в себе никакого смысла порядка. Если вы заботитесь о порядке, проясните это и используйте обычныйfor
цикл с индексом.источник
Главным образом две причины:
Один
Как уже говорили другие, вы можете получить ключи, которых нет в вашем массиве или которые унаследованы от прототипа. Итак, если, скажем, библиотека добавляет свойство к прототипам Array или Object:
Вы получите его как часть каждого массива:
Вы можете решить это с помощью метода hasOwnProperty:
но это верно для итерации любого объекта с циклом for-in.
Два
Обычно порядок элементов в массиве важен, но цикл for-in не обязательно повторяется в правильном порядке, потому что он обрабатывает массив как объект, как это реализовано в JS, а не как массив. Это кажется небольшой вещью, но она действительно может испортить приложения и ее трудно отладить.
источник
Object.keys(a).forEach( function(item) { console.log(item) } )
переберите массив собственных ключей свойств, а не тех, которые унаследованы от прототипа.array.forEach
, вставив определенный код в ваши скрипты. См. Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Потому что он перечисляет через поля объекта, а не индексы. Вы можете получить значение с индексом "длина", и я сомневаюсь, что вы хотите этого.
источник
Я не думаю, что мне есть что добавить, например. Ответ Триптиха или ответ CMS о том, почему
for...in
в некоторых случаях следует избегать использования.Однако я хотел бы добавить, что в современных браузерах есть альтернатива,
for...in
которую можно использовать в тех случаях, когдаfor...in
ее нельзя использовать. Эта альтернативаfor...of
:Замечания :
К сожалению, ни одна версия Internet Explorer не поддерживает
for...of
( Edge 12+ ), поэтому вам придется подождать немного дольше, пока вы не сможете использовать ее в своем рабочем коде на стороне клиента. Тем не менее, он должен быть безопасным для использования в вашем JS-коде на стороне сервера (если вы используете Node.js ).источник
for-of
, не такfor-in
ли?Проблема с
for ... in ...
- и это становится проблемой только тогда, когда программист действительно не понимает язык; на самом деле это не ошибка или что-то в этом роде - это то, что она перебирает все элементы объекта (ну, все перечисляемые элементы, но пока это подробно). Когда вы хотите выполнить итерации только по индексированным свойствам массива, единственный гарантированный способ сохранить семантически непротиворечивые вещи - это использовать целочисленный индекс (то естьfor (var i = 0; i < array.length; ++i)
цикл стиля).Любой объект может иметь произвольные свойства, связанные с ним. В частности, не было бы ничего страшного в загрузке дополнительных свойств в экземпляр массива. Код, который хочет видеть только индексированные свойства массива, должен придерживаться целочисленного индекса. Код, который полностью осведомлен о том, что
for ... in
делает и действительно должен видеть все свойства, ну тогда это тоже нормально.источник
for in
, по сравнению с обычным циклом for, эти массивы были бы перебраны? (что по сути будет медленной производительностью, верно?)in
вfor ... in
цикле будет простоfor ... in
массивы; Есть много веских причин не делать этого. Это не столько проблема производительности, сколько проблема «убедитесь, что она не сломалась».[{a:'hey',b:'hi'},{a:'hey',b:'hi'}]
но да, я понимаю.Кроме того, из-за семантики способ обработки
for, in
массивов (т. Е. Так же, как и любого другого объекта JavaScript) не согласован с другими популярными языками.источник
TL & DR: использование
for in
цикла в массивах не является злом, на самом деле это совсем наоборот.Я думаю, что
for in
цикл является жемчужиной JS, если он используется правильно в массивах. От вас ожидают, что вы полностью контролируете свое программное обеспечение и знаете, что делаете. Давайте посмотрим на упомянутые недостатки и опровергаем их один за другим.Array.prototype
должны были быть сделаны с помощьюObject.defineProperty()
и ихenumerable
дескриптор должен быть установлен вfalse
. Любая библиотека, не делающая этого, не должна использоваться вообще.Object.setPrototypeOf
по классу или по классуextend
. Вы должны снова использоватьObject.defineProperty()
которые наборами по умолчанию вwritable
,enumerable
иconfigurable
дескрипторов свойств дляfalse
. Давайте посмотрим пример подклассификации массива здесь ...Итак, вы видите ...
for in
цикл теперь безопасен, так как вы заботились о своем коде.for in
петля медленно: Ад нет. Это самый быстрый способ итерации, если вы зацикливаетесь на разреженных массивах, которые время от времени необходимы. Это один из самых важных приемов производительности, которые нужно знать. Давайте посмотрим на пример. Мы будем зацикливаться на разреженном массиве.источник
for in
цикл затмевает другие, «если» массив редок, и чем больше, тем больше его размер. Поэтому я переставил стендовый тест для разреженного массива,arr
размером ~ 10000, только с 50 предметами, случайно выбранными из[42,"test",{t:1},null, void 0]
случайных индексов. Вы сразу заметите разницу. - >> Проверьте это здесь << - .В дополнение к другим проблемам, синтаксис for..in, вероятно, медленнее, поскольку индекс представляет собой строку, а не целое число.
источник
var i in a
и ожидаете, что индекс будет целочисленным, то выполнение чего-то подобногоa[i+offset] = <value>
будет помещать значения в совершенно неправильные места. («1» + 1 == «11»).Важным аспектом является то, что
for...in
выполняется только перебор свойств, содержащихся в объекте, для атрибута перечисляемого свойства которого установлено значение true. Таким образом, если кто-то пытается перебрать объект, используя,for...in
тогда могут быть пропущены произвольные свойства, если их атрибут перечислимого свойства равен false. Вполне возможно изменить атрибут перечислимого свойства для обычных объектов Array, чтобы определенные элементы не перечислялись. Хотя в целом атрибуты свойств обычно применяются к свойствам функций внутри объекта.Можно проверить значение атрибута перечисляемого свойства свойств:
Или получить все четыре атрибута свойства:
Эта функция доступна в ECMAScript 5 - в более ранних версиях было невозможно изменить значение атрибута перечисляемого свойства (для него всегда было установлено значение true).
источник
for
/in
Работает с двумя типами переменных: HashTables (ассоциативные массивы) и массив (не ассоциативной).JavaScript автоматически определит способ прохождения элементов. Поэтому, если вы знаете, что ваш массив действительно неассоциативен, вы можете использовать его
for (var i=0; i<=arrayLen; i++)
и пропустить итерацию автоопределения.Но, по моему мнению, лучше использовать
for
/in
, процесс, необходимый для этого автоопределения, очень мал.Реальный ответ на этот вопрос будет зависеть от того, как браузер анализирует / интерпретирует код JavaScript. Это может измениться между браузерами.
Я не могу думать о других целях, чтобы не использовать
for
/in
;источник
Array
этоObject
слишкомfor ... in
работает с объектами. Там нет такого понятия, как автоопределение.Потому что он будет перебирать свойства, принадлежащие объектам вверх по цепочке прототипов, если вы не будете осторожны.
Вы можете использовать
for.. in
, просто не забудьте проверить каждое свойство с hasOwnProperty .источник
true
приhasOwnProperty()
проверках.isNaN
проверки каждого имени свойства.isNaN
для проверки, является ли переменная специальным значением NaN или нет, она не может использоваться для проверки «вещей, отличных от чисел» (вы можете пойти с обычным typeof для этого).Это не обязательно плохо (в зависимости от того, что вы делаете), но в случае массивов, если что-то было добавлено
Array.prototype
, вы получите странные результаты. Где вы ожидаете, что этот цикл будет выполняться три раза:Если функция называется
helpfulUtilityMethod
добавленаArray
«сprototype
, то ваш цикл будет в конечном итоге работает в четыре раза:key
было бы0
,1
,2
иhelpfulUtilityMethod
. Если бы вы ожидали только целые числа, упс.источник
Вы должны использовать
for(var x in y)
только в списках свойств, а не на объектах (как описано выше).источник
Использование
for...in
цикла для массива не является неправильным, хотя я могу догадаться, почему кто-то сказал вам, что:1.) Уже есть функция или метод более высокого порядка, который имеет эту цель для массива, но имеет больше функциональности и более тонкий синтаксис, называемый «forEach»:
Array.prototype.forEach(function(element, index, array) {} );
2.) Массивы всегда имеют длину, но
for...in
иforEach
не выполняют функции для любого значения, которое'undefined'
только для индексов , которые имеют значение , определенное. Таким образом, если вы присваиваете только одно значение, эти циклы будут выполнять функцию только один раз, но, поскольку массив перечисляется, он всегда будет иметь длину до самого высокого индекса, который имеет определенное значение, но эта длина может остаться незамеченной при использовании этих значений. петли.3.) Стандарт цикла for будет выполнять функцию столько раз, сколько вы определяете в параметрах, и, поскольку массив нумеруется, имеет смысл определить, сколько раз вы хотите выполнить функцию. В отличие от других циклов, цикл for может затем выполнять функцию для каждого индекса в массиве, независимо от того, определено это значение или нет.
По сути, вы можете использовать любой цикл, но вы должны точно помнить, как они работают. Понять условия, при которых повторяются различные циклы, их отдельные функции и понять, что они будут более или менее подходящими для различных сценариев.
Кроме того, может считаться лучшей практикой использовать
forEach
метод, чемfor...in
цикл в целом, потому что он легче писать и имеет больше функциональных возможностей, поэтому вы можете захотеть использовать только этот метод и стандарт для, но ваш вызов.Ниже показано, что первые два цикла выполняют операторы console.log только один раз, в то время как стандарт цикла выполняет функцию столько раз, сколько указано, в данном случае, array.length = 6.
источник
Вот причины, по которым это (обычно) плохая практика:
for...in
циклы перебирают все свои перечисляемые свойства и перечислимые свойства своих прототипов. Обычно в итерации массива мы хотим перебирать только сам массив. И даже если вы сами ничего не можете добавить в массив, ваши библиотеки или фреймворк могут что-то добавить.Пример :
for...in
циклы не гарантируют определенный порядок итераций . Хотя этот порядок обычно наблюдается в большинстве современных браузеров, гарантия 100% все еще отсутствует.for...in
Циклы игнорируютundefined
элементы массива, то есть элементы массива, которые еще не были назначены.Пример :
источник
for ... in полезен при работе с объектом в JavaScript, но не для Array, но все же мы не можем сказать, что это неправильный путь, но это не рекомендуется, посмотрите на этот пример ниже, используя цикл for ... in :
Хорошо, теперь сделаем это с Array :
Как видите результат тот же ...
Но давайте попробуем что-то, давайте создадим что-то для Array ...
Теперь мы создаем новый массив ();
Вы видите кого- то другого ... ... В этом случае мы фактически перебираем новый объект Array!
Так что это одна из причин, почему мы должны использовать для ... осторожно, но это не всегда так ...
источник
Цикл for ... in всегда перечисляет ключи. Ключи свойств объектов всегда String, даже индексированные свойства массива:
источник
Из https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections
источник
хотя этот вопрос конкретно не рассматривается, я хотел бы добавить, что есть очень веская причина не использовать для ... в с
NodeList
(как можно было бы получить изquerySelectorAll
вызова, поскольку он вообще не видит возвращаемые элементы, вместо этого итерация только по свойствам NodeList.в случае одного результата я получил:
который объяснил, почему мой
for (node in nodes) node.href = newLink;
провал.источник