Выбрать главу

Ниже приведен пример того, как это можно осуществить:

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();

// запускаем первый тест, обычный перебор элементов массива,