Javascript: эффективен ли метод длины?
Я занимаюсь кодировкой javascript, и мне было интересно, является ли метод длины "предварительно вычисленным" или запоминается JS-движком.
Итак, вопрос:
Если я часто проверяю длину массива и полагаю, что я не изменяю его (делая его неизменным через закрытие), должен ли я прекомпретировать метод длины и сохранить его в некоторой переменной?
Спасибо!
Ответы
Ответ 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;
}