Ниже приведен пример того, как это можно осуществить:
function Iterate(aEntries) {
for (var i=0; i < document.getElementsByTagName(‘h1’).length; i++) {
aEntries[aEntries.length] =
document.getElementsByTagName(‘h1’)[i].innerText;
}
}
Что плохого в приведенном примере? Он содержит два обращения к массиву document.getElementsByTagName(‘h1’) на каждой итерации. Внутри цикла наш скрипт будет:
вычислять размер массива;
получать значение свойства innerText для текущего элемента в массиве.
Это совершенно не эффективно. Дополнительные вычисления, связанные с многочисленными ненужными обращениями к DOM-дереву для получения информации, которую мы и так знаем, являются совершенно лишними. Давайте рассмотрим следующую модификацию этого скрипта:
function Iterate2(aEntries) {
var oH1 = document.getElementsByTagName(‘h1’);
var iLength = oH1.length;
for (var i=0; i < iLength; i++) {
aEntries[aEntries.length] = oH1(i).innerText;
}
}
Таким образом, мы кэшируем DOM-массив в локальную переменную, и затем все действия над ней производятся гораздо быстрее. N обращений к DOM-дереву превращается всего в одно-единственное в результате использования кэширования.
Кэширование ресурсоемких вызовов
Как показывает практика, лучше всего будет использовать переменные максимально близко к области их объявления и избегать использования глобальных переменных любой ценой. Глобальная область видимости обычно содержит десятки, если не сотни объектов, которые добавлены браузером согласно спецификации и для собственных нужд, поэтому обращения к глобальным переменным всегда ресурсоемки.
Также стоит с осторожностью использовать ключевое слово with, так как оно не дает компилятору генерировать код для быстрого доступа к локальным переменным (ему приходится сначала пробежаться по цепочке прототипа объекта, затем по цепочке вышестоящей области видимости и т. д.).
Если у вас есть примерно такой участок кода:
var arr = ...;
var globalVar = 0;
(function () {
var i;
for (i = 0; i < arr.length; i++) {
globalVar++;
}
})();
то его можно оптимизировать следующим образом:
var arr = ...;
var globalVar = 0;
(function () {
var i, l, localVar;
l = arr.length;
localVar = globalVar;
for (i = 0; i < l; i++) {
localVar++;
}
globalVar = localVar;
})();
В этом примере мы уменьшили число обращений к глобальной переменной и устранили расчет размера массива на каждой итерации цикла.
Кэшируем цепочки вызовов
Распознавание (разрешение) ссылки на объект или метод выполняется каждый раз, когда происходит обращение к этому объекту или методу. Переменные разрешаются всегда в обратном порядке: от более частной области видимости к более общей. Поэтому, если у нас есть примерно следующий код:
for (i=0; i < 10000; i++) a.b.c.d(v);
то он будет выполняться несколько медленнее, чем
var f=a.b.c.d;
for (i=0; i < 10000; i++) f(v);
или
var f=a.b.c;
for (i=0; i < 10000; i++) f.d(v);
На данный момент браузеры хорошо справляются с кэшированием вызовов функций, и особого прироста производительности при такой оптимизации в IE и Firefox не наблюдается. При проектировании приложений для других браузеров стоит учитывать и этот аспект, однако с большой вероятностью кэширование вызовов объектов уже добавлено (или будет добавлено в самое ближайшее время) в разрабатываемые JavaScript-движки, ибо это одно из наиболее узких мест в производительности веб-приложений.
Наконец, при использовании кэширования (особенно частей DOM-дерева) стоит помнить, что оно уменьшает использование CPU, но увеличивает расходование памяти (и может привести к псевдо-утечкам), поэтому в каждом конкретном случае нужно выбирать меньшее из двух зол.
7.8. Быстрые итераторы, регулярные выражения и другие вкусности
В этом разделе собраны некоторые практические советы по производительности отдельных конструкций в JavaScript-движках в браузере.
Итераторы
Давайте рассмотрим, какой способ перебора элементов будет максимально быстрым в JavaScript. У нас есть несколько возможностей для реализации цикла, ниже приведен полный вариант кода для тестирования.
<!-- набор элементов для результатов тестирования -->
<p id="test1"></p>
<p id="test2"></p>
<p id="test3"></p>
<p id="test4"></p>
<p id="test5"></p>
<p id="test6"></p>
<script type="text/javascript">
// выбираем все элементы из DOM-дерева
var items = document.getElementsByTagName("*");
// кэшируем текущий размер DOM-дерева
var length = items.length;
// запоминаем текущий момент времени
var time = new Date().getTime();
// запускаем первый тест, обычный перебор элементов массива,