Подтвердить что ты не робот

Javascript: эффективен ли метод длины?

Я занимаюсь кодировкой javascript, и мне было интересно, является ли метод длины "предварительно вычисленным" или запоминается JS-движком.

Итак, вопрос:

Если я часто проверяю длину массива и полагаю, что я не изменяю его (делая его неизменным через закрытие), должен ли я прекомпретировать метод длины и сохранить его в некоторой переменной?

Спасибо!

4b9b3361

Ответ 1

Все основные интерпретаторы предоставляют эффективные средства доступа для длин собственных массивов, но не для подобных массиву объектов, таких как NodeList s.

"Эффективный цикл в Javascript"

Test / Browser                Firefox 2.0 Opera 9.1   Internet Explorer 6
Native For-Loop               155 (ms)    121 (ms)    160 (ms)
...
Improved Native While-Loop    120 (ms)    100 (ms)    110 (ms)

"Эффективный код JavaScript" предлагает

for( var i = 0; i < document.getElementsByTagName('tr').length; i++ ) {
  document.getElementsByTagName('tr')[i].className = 'newclass';
  document.getElementsByTagName('tr')[i].style.color = 'red';
  ...
}

var rows = document.getElementsByTagName('tr');
for( var i = 0; i < rows.length; i++ ) {
  rows[i].className = 'newclass';
  rows[i].style.color = 'red';
  ...
}

Ни один из них не эффективен. getElementsByTagName возвращает динамический объект, а не статический массив. Каждый раз, когда условие цикла проверяется, Opera должна переоценить объект и определить, сколько элементов он ссылается, чтобы выработать свойство length. Это занимает немного больше времени, чем проверка на статическое число.

Ответ 2

Как всегда, ответ "зависит".

Позвольте протестировать собственные массивы с массивом из миллиона элементов:

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

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

http://josh3736.net/images/arrlen.png

Chrome и Firefox оптимизируют свойство accessor, чтобы он был таким же эффективным, как копирование длины в локальную переменную. IE и Opera не работают и на 50% + медленнее.

Однако, помните, что результаты теста "ops/second" означают количество полных итераций через массив из миллиона элементов в секунду.

Чтобы представить это в перспективе, даже в IE8 (худший исполнитель в этой группе), который набрал 0,44 и 3,9 по доступу к ресурсам и локальной переменной (соответственно), а за итерационный штраф был меньше 2 мкс. Итерируя более тысячи элементов, использование array.length будет стоить вам дополнительно 2 мс. Другими словами: остерегайтесь преждевременной оптимизации.

Ответ 3

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

http://jsperf.com/length-comparisons

Исключением является объект nodeList, возвращаемый DOM из функций типа getElementsByTagName() или getElementsByClassName(). В них часто гораздо медленнее обращаться к свойству length. Вероятно, это связано с тем, что эти объекты nodeList не являются истинными объектами javascript, и может существовать мост между Javascript и собственным кодом, который должен пересекаться каждый раз, когда к ним обращаются какие-либо объекты. В этом случае было бы LOT быстрее (на 10-100 раз быстрее) кэшировать длину в локальную переменную, а не использовать ее повторно в цикле с nodeList. Я добавил это к сравнению длины, и вы можете видеть, насколько он медленнее.

В некоторых браузерах значительно проще поместить длину в локальную переменную и использовать ее оттуда, если вы будете ссылаться на нее снова и снова (например, в цикле). Здесь график производительности приведенного выше теста jsperf:

Ответ 4

Вероятно, небольшое ускорение скорости достигается путем кэширования длины в локальной переменной из-за скорости поиска атрибутов. Это может быть или не быть незначительным, в зависимости от того, как JS движет JIT код.

См. http://jsperf.com/for-loop-caching для рудиментарного тестового теста JSperf.

Ответ 5

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

var elems = document.getElementsByName("tst");
var elemsLen = elems.length;
var i;
for(i = 0; i < elemsLen; ++i)
{
  // work with elems... example:
  // elems[i].selected = false;
}
elems = [10,20,30,40,50,60,70,80,90,100];
elemsLen = elems.length;
for(i = 0; i < elemsLen; ++i)
{
  // work with elems... example:
  // elems[i] = elems[i] / 10;
}