Проводят ли петли каждый раз при проверке массива array.length при массиве array.length?

Я просматривал, и я нашел это:

var i, len;
for(i = 0, len = array.length; i < len; i++) {  
   //...
}

Мои первые мысли:

  • Почему он это сделал? (по какой-то причине это должно быть лучше)
  • Стоит ли это? (Я полагаю, да, почему еще он это сделает?)

Выполняют ли обычные циклы (те, которые не кэшируют длину) array.length каждый раз?

Ответ 1

Цикл, состоящий из трех частей, выполняется следующим образом:

for (A; B; C)

A - Executed before the enumeration
B - condition to test
C - expression after each enumeration (so, not if B evaluated to false)

Итак, да: свойство .length массива проверяется при каждом перечислении, если оно построено как for(var i=0; i<array.length; i++). Для микро-оптимизации эффективно хранить длину массива во временной переменной (см. Также: Каков самый быстрый способ перебора массива в JavaScript?).

Эквивалентно for (var i=0; i<array.length; i++) { ... }:

var i = 0;
while (i < array.length) {
    ...
    i++;
}

Ответ 2

Is it worth it? (obviously yes, why else he will do it this way?)

Абсолютно да. Потому что, как вы говорите, цикл будет каждый раз вычислять длину массива. Таким образом, это вызовет огромные накладные расходы. Выполните следующие фрагменты кода в вашем firebug или hrome dev tool vs.

// create an array with 50.000 items
(function(){
    window.items = [];
    for (var i = 0; i < 50000; i++) {
        items.push(i);
    }
})();

// a profiler function that will return given function execution time in milliseconds
var getExecutionTime = function(fn) {
    var start = new Date().getTime();
    fn();
    var end = new Date().getTime();
    console.log(end - start);
}

var optimized = function() {
    var newItems = [];
    for (var i = 0, len = items.length; i < len; i++) {
        newItems.push(items[i]);
    }
};


var unOptimized = function() {
    var newItems= [];
    for (var i = 0; i < items.length; i++) {
        newItems.push(items[i]);
    }
};

getExecutionTime(optimized);
getExecutionTime(unOptimized);

Ниже приведены приблизительные результаты в различных браузерах.

Browser    optimized    unOptimized
Firefox    14           26
Chrome     15           32
IE9        22           40
IE8        82           157
IE7        76           148 

Так что подумайте еще раз и используйте оптимизированный способ:)
Примечание.. Я пытался работать с этим кодом на jsPerf, но теперь я не смог получить доступ к jsPerf. Наверное, это было, когда я пытался.

Ответ 3

  • если array.length является вычисленным значением (маловероятным в вашем примере), тогда лучше получить значение один раз и сохранить его в переменной.

  • если array.length не вычисляется (вероятно, случай в вашем примере), тогда нет значения для извлечения длины и сохранения в переменной.

Ответ 5

Одна из причин этого - сказать, если вы добавляете элементы в массив во время цикла, но не хотите их перебирать. Скажем, вы хотите повернуть [1, 2, 3] в [1, 2, 3, 1, 2, 3]. Вы могли бы с этим:

var initialLength = items.length;
for(var i=0; i<initialLength; ++i) {
    items.push(items[i]);
}

Если вы не сохраняете длину перед циклом, то array.length будет продолжать увеличиваться, и цикл будет работать до тех пор, пока браузер не выйдет из строя/не ударит его.

Кроме того, как говорили другие, это мягко влияет на производительность. У меня не было бы привычки делать это, потому что "преждевременная оптимизация - это корень всего зла". Кроме того, если вы измените размер массива во время цикла, это может привести к повреждению вашего кода. Например, если вы удаляете элементы из массива во время цикла, но продолжаете сравнивать i с предыдущим размером массива, тогда цикл попытается получить доступ к элементам за пределами нового размера.

Ответ 6

Я всегда думал, что длина JavaScript - это просто свойство объекта массива, предварительно рассчитанное предыдущими операциями массива - создание, добавление, удаление или переопределение пользователем, так что вы просто просматриваете переменную в любом случае? Должен признаться, я только что предположил, что из-за отсутствия скобок, но, глядя на страницу MDN для array.length, кажется, что то же самое.

В языках, где длина - это метод или длина, вычисляется стандартной библиотечной функцией, вы должны предварительно вычислить длину перед запуском цикла, чтобы массив не вычислялся на каждую итерацию, особенно для больших наборов данных. Даже тогда, в современных языках высокого уровня, таких как Python, len() просто возвращает свойство length объекта массива в любом случае.

Поэтому, если я не ошибаюсь, сложность - это просто O (1), и с этой точки зрения, даже если переменная была немного быстрее, чем свойство для поиска каждого прохода, это не стоило бы потенциальных проблем с созданием/повторное использование дополнительных переменных за пределами области защиты для цикла.

Тем не менее, я подозреваю, что в этом случае причина, по которой программист-программист выбрал этот подход, - это просто привычка, которую они выбрали на другом языке и перенесла JavaScript.