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

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

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

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

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

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

А также:

for (var i in myArray)

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

24 ответа

Решение

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

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

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

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

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

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

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

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

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

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

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


JQuery-х each(callback) метод использует for( ; ; ) цикл по умолчанию, и будет использовать for( in ) только если длина undefined,

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

Пример:

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

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


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

Например:

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

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

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

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

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

Обновленный ответ для текущей версии всех основных браузеров 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.

Использование 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]);
});

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

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.
}

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

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

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

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

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

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

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';
  }
});

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

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

Используйте 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Быть осторожен!!! Я использую 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

Используйте for in для перебора объекта. Он имеет более короткий синтаксис и прост в использовании. Здесь слово происходит от index. Рассматривайте свойства объекта как индекс и перебирайте его. Если вы используете for in для массива, вы получите индекс элементов, присутствующих внутри массива.

Между ними есть важное различие. 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(;;).

Другие вопросы по тегам