Кэширование в JavaScript

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

Итерации и локальное кэширование

При DOM-операциях перебор массива объектов является довольно типичной задачей. Давайте предположим, что вы разрабатываете HTML-приложение, которое индексирует содержание страниц. Нашей задачей является сбор всех элементов h1 на текущей странице, чтобы затем использовать их в проиндексированном массиве.

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

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, но увеличивает расходование памяти (и может привести к псевдо-утечкам), поэтому в каждом конкретном случае нужно выбирать меньшее из двух зол.

Материалы близкой тематики:

Posted in Разгони свой сайт.