Как лучше всего делать циклы в JavaScript

Я наткнулся на несколько методов зацикливания в JavaScript, что мне больше всего нравится:

for(var i = 0; i < a.length; i++){
    var element = a[i];
}

Но как проверено здесь (http://www.robertnyman.com/2008/04/11/javascript-loop-performance/), вероятно, следует написать так, чтобы длина вычислялась только один раз.

В jQuery есть .each, в который можно втыкать функцию. Мне это нравится немного больше, потому что мне не нужно вводить массив дважды, как в приведенном выше решении.

Если бы JavaScript поддерживал макросы, было бы легко создать свой собственный, но, к сожалению, это не так.

Так что вы, ребята, используете?


person Anders Rune Jensen    schedule 11.10.2008    source источник
comment
По-видимому, то, что вы подразумеваете под лучшим, является самым чистым, верно? Кто-то может сказать, что лучший способ - сделать это самым быстрым способом, и в этом случае ответ, вероятно, будет другим...   -  person Jason Bunting    schedule 11.10.2008
comment
Да хорошо. Я выбрал слово «лучший», потому что мне нужно было одно решение для циклов, которое я мог бы использовать всегда, не задумываясь об этом. Таким образом, он не должен быть ужасно медленным при работе со 100 тыс. элементов, он не должен быть ужасно длинным для записи и т. д.   -  person Anders Rune Jensen    schedule 11.10.2008


Ответы (9)


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

function createIterator(x) {
    var i = 0;

     return function(){
       return x[i++];
    };
}

Затем использовать:

var iterator=createIterator(['a','b','c','d','e','f','g']);

iterator();

возвращает «а»;

iterator();

возвращает «б»;

и так далее.

Чтобы перебрать весь список и отобразить каждый элемент:

var current;

while(current=iterator())
{
    console.log(current);
}

Имейте в виду, что приведенное выше приемлемо только для итерации списка, содержащего «неложные» значения. Если этот массив содержит что-либо из:

  • 0
  • ложный
  • ""
  • нулевой
  • NaN

предыдущий цикл остановится на этом элементе, а не всегда на том, что вы хотите/ожидаете.

Чтобы избежать этого использования:

var current;

while((current=iterator())!==undefined)
{
   console.log(current);
}
person Ash    schedule 11.10.2008
comment
Да, я также думал об использовании итераторов. Они намного лучше инкапсулируют концепцию обхода чего-либо, чем это делают простые циклы. Но как бы вы в своем примере распечатали все элементы в итераторе? - person Anders Rune Jensen; 11.10.2008
comment
Красивая сладость закрытия. Ааа, я люблю их. - person Jason Bunting; 11.10.2008
comment
Как узнать, что вы достигли конца, если ваш итератор не имеет функции HasNext? Если вы просто продолжите вызывать итератор в соответствии с вашим примером, вы в конечном итоге получите индекс массива за пределы. - person Kibbee; 11.10.2008
comment
Кибби, Андерс, я добавил простой пример того, как перебрать весь список в ответ. - person Ash; 11.10.2008
comment
Извините, я думаю, я немного приучен к использованию языков, которые генерируют исключения, когда вы пытаетесь получить доступ за конец массива, чтобы попытаться закодировать что-то, что основано на том факте, что JS не генерирует исключение, когда вы пытаетесь сделай это. - person Kibbee; 11.10.2008
comment
Kibbee, Не нужно извиняться, я должен был добавить этот пример раньше. Одна из самых важных вещей, которые нужно знать в Javascript, — это ложные значения (значения, которые оцениваются как ложные). Это: , null, 0, NaN, false и, наконец, undefined. Итератор полагается на возврат undefined. - person Ash; 11.10.2008
comment
Если вам понравился этот ответ, вас может заинтересовать bob. pythonmac.org/archives/2005/07/06/iteration-in-javascript и Mochikit mochikit.com/doc/html/MochiKit/Iter.html - person Jonny Buchanan; 11.10.2008
comment
Это довольно хакерски. Мне нравится это! - person Manuel Ferreria; 07.01.2009
comment
Добро пожаловать в мир Slow and Confusing. Эта идиома далеко не распространена в мире JavaScript, поэтому у людей возникнут трудности с кодовой базой, если вы решите ее использовать. Если у вас есть большие циклы, этот подход также очень медленный. Вызовы функций дороги в JS. - person Jani Hartikainen; 21.01.2010
comment
@Jani, добро пожаловать в мир Новых Идей! Этот ответ может помочь сделать идиому более распространенной. Кроме того, по моему опыту, производительность более чем приемлема для обычного повседневного использования, и в любом случае это сильно зависит от движка Javascript, на котором он работает. Вызовы функций могут быть дорогими, но неподдерживаемый Javascript стоит чертовски дороже! - person Ash; 21.01.2010
comment
Стандартную петлю вряд ли можно обслуживать. Однако я вижу, что вы поняли мою мысль;) - person Jani Hartikainen; 21.01.2010
comment
Мне нравится сексуальное использование замыканий, хотя я вряд ли верю, что это лучший способ зацикливания массива в Javascript. Стандартный цикл for с кешированной длиной по-прежнему остается самым быстрым и гибким способом IMHO. Но для обычного повседневного дерьма это должно быть нормально, хорошая работа;) - person mynameistechno; 27.08.2011

Небольшое улучшение оригинала, чтобы вычислить размер массива только один раз:

for(var i = 0, len = a.length; i < len; i++){ var element = a[i]; }

Кроме того, я вижу много циклов for..in. Хотя имейте в виду, что технически это не кошерно и вызовет проблемы именно с Prototype:

for (i in a) { var element = a[i]; }
person Chase Seibert    schedule 11.10.2008
comment
Циклы for..in используются для перебора свойств объекта, хотя они, кажется, работают для массивов, они также будут перебирать свойство «длина» или любое другое динамически добавляемое свойство. Вот почему это плохо работает с Prototype. - person Vincent Robert; 12.10.2008
comment
второй пример i, вероятно, создает глобальный - person ajax333221; 24.05.2012

Просто сначала сохраните длину в переменной.

  var len = a.length;
  for (var i = 0; i < len; i++) {
    var element = a[i];
  }
person Randy Sugianto 'Yuku'    schedule 11.10.2008

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

Очень похоже на @Mr. Ондатра - но упрощая тест:

var i = a.length, element = null;
while (i--) {
  element = a[i];
}
person Remy Sharp    schedule 14.10.2008
comment
ну, я очень опаздываю на вечеринку, но это правильный ответ, и его следует принять как таковой. Для непосвященных предложение i-- сохраняет сравнение (поскольку 0 = false в тестах JS). Предостережение 1: обратный порядок! Предостережение 2: читабельность невелика. Предостережение 3: кешированный цикл for почти так же хорош - person annakata; 07.01.2009

Вы всегда можете использовать цикл while и заранее вычислить предел массива.

Var max = a.length-1;
var i = 0;

while(i <= max)
{
var element = a[i];
i++;
}
person Kibbee    schedule 11.10.2008
comment
Извините за прямоту, но я не вижу, как это улучшение. Вы по-прежнему указываете массив дважды, и в вашем решении среда вокруг загрязнена двумя переменными, масштаб которых не должен выходить за пределы конструкции цикла while. - person Anders Rune Jensen; 11.10.2008
comment
Окружающая среда в любом случае будет загрязнена, учитывая, что в Javascript областью действия переменной является объемлющая функция, а не блок, в котором она объявлена. - person Jason Etheridge; 11.10.2008
comment
Тестируя его по ссылке, указанной в вопросе, он, по крайней мере, так же быстр, если не быстрее, чем любая из реализаций цикла for. - person Kibbee; 11.10.2008

Если у вас много элементов в массиве и скорость является проблемой, вы можете использовать цикл while, который повторяется от самого высокого к самому низкому.

  var i = a.length;
  while( --i >= 0 ) {
    var element = a[i];
    // do stuff with element
  }  
person Mr. Muskrat    schedule 11.10.2008
comment
›= 0 не нужен, просто измените --i на i-- - person Paul Hargreaves; 15.01.2010

Я не использую его сам, но один из моих коллег использует этот стиль:

var myArray = [1,2,3,4];
for (var i = 0, item; item = myArray[i]; ++i) {
    alert(item);
}

как и ответ Эша, это вызовет проблемы, если в вашем массиве есть «ложные» значения. Чтобы избежать этой проблемы, измените его на (item = myArray[i]) != undefined

person nickf    schedule 12.10.2008

Я не вижу проблемы с использованием стандартного цикла for(;;). Небольшой тест

var x;
var a = [];
// filling array
var t0 = new Date().getTime();
for( var i = 0; i < 100000; i++ ) {
    a[i] = Math.floor( Math.random()*100000 );
}

// normal loop
var t1 = new Date().getTime();
for( var i = 0; i < 100000; i++ ) {
    x = a[i];
}

// using length
var t2 = new Date().getTime();
for( var i = 0; i < a.length; i++ ) {
    x = a[i];
}

// storing length (pollution - we now have a global l as well as an i )
var t3 = new Date().getTime();
for( var i = 0, l = a.length; i < l; i++ ) {
    x = a[i];
}

// for in
var t4 = new Date().getTime();
for( var i in a ) {
    x = a[i];
}

// checked for in
var t5 = new Date().getTime();
for( var i in a ) {
    if (a.hasOwnProperty(i)) {
        x = a[i];
    }
}

var t6 = new Date().getTime();
var msg = 'filling array: '+(t1-t0)+'ms\n'+
          'normal loop: '+(t2-t1)+'ms\n'+
          'using length: '+(t3-t2)+'ms\n'+
          'storing length: '+(t4-t3)+'ms\n'+
          'for in: '+(t5-t4)+'ms\n'+
          'checked for in: '+(t6-t5)+'ms';
console.log( msg );

приводит к:

filling array: 227ms
normal loop: 21ms
using length: 26ms
storing length: 24ms 
for in: 154ms
checked for in: 176ms

Итак: - для того, чтобы занять самое длинное, использование свойства длины (которое является свойством и его не нужно вычислять) почти так же быстро, как и его сохранение в первую очередь, что всего лишь немного медленнее, чем использование целого числа.
И for() — это обычный способ обхода массива в цикле, который все ожидают и понимают.

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

person meouw    schedule 07.01.2009

Итак, сначала вы определите идеальный цикл javascript, я считаю, что он должен выглядеть так:

ary.each(функция() {$arguments[0]).remove();})

Для этого может потребоваться библиотека Prototo.js.

Затем вы получаете отвращение к части arguments[0], и код создается автоматически из вашей серверной среды. Это работает, только если лестница Морская.

Теперь у вас есть вышеизложенное, сгенерированное:

ари делать: [:каждый | каждый элемент удалить].

Это идет в комплекте с завершением синтаксиса и точно переводится в приведенный выше javascript. И это вскружит голову людям, которые раньше не использовали интеграцию прототипов seasides, когда они будут читать ваш код. Это, безусловно, заставляет вас чувствовать себя круто, тоже. Не говоря уже о том, что здесь вы можете получить больше компьютерных навыков. Девочки в восторге!

person nes1983    schedule 07.01.2009