Разгоняем jQuery. Часть 1
Статей по ускорению jQuery достаточно много, но обычно они не отличаются наглядностью и подробностью, поэтому я решил провести несколько тестов и выделить, те советы по ускорению jQuery, которые действительно работают.
Кеширование
Кеширование результатов выборки заложено в типовую конструкцию применения jQuery.
Цепочка вызовов:
$('#id-of-element').attr('data-value', 'data-for-element')
.css('color', 'red')
.html('<p>html code</p>');
эквивалентна, следующему коду:
var element = $('#id-of-element');
element.attr('data-value', 'data-for-element');
element.css('color', 'red');
element.html('<p>html code</p>');
Уже здесь на одну выборку приходится несколько вызовов методов, и нам остается только расширить эту практику дальше одной цепочки, например, для выноса выборки за пределы цикла:
var el = $('#el-999-9');
for (var i = 0; i
или обработчика события:
var el = $('#el-999-9');
$('#button').click(function(){
el.empty()
.html('html code');
});
Кеширование эффективно даже на примере самого быстрого селектора #id:
Код тестов:
var runCount = 100;
function testIdLoopNoCache()
{
for (var i = 0; i
Хороший результат, но давайте проверим селектор посложнее, например, .class1 .class2:
Разница в производительности браузеров настолько велика, что трудно показать их результаты
на одном графике, поэтому выделим еще один график для самых быстрых (по крайней
мере в этом тесте):
Очевидно, что чем медлительней селектор тем эффективнее кеширование, особенно актуально кеширование выборок в браузерах, не поддерживающих querySelectorAll.
Минимизируйте работу с DOM
Работа с DOM по возможности должна сводиться к минимуму, например, при добавлении
нескольких элементов нужно сначала объединить их код, а потом вставить одним вызовом
html.
Начнем с тестов:
Код тестов:
function testAppendLi()
{
$('#cnt').append('<ol id="list"></ol>');
for (var i = 0; i < runCount; i++)
{
$('#list').append('<li>' + i + '</li>');
}
}
function testAppendLiCacheSelection()
{
$('#cnt').append('<ol id="list"></ol>');
var list = $('#list');
for (var i = 0; i < runCount; i++)
{
list.append('<li>' + i + '</li>');
}
}
function testAppendAllLi()
{
$('#cnt').append('<ol id="list"></ol>');
var list = '';
for (var i = 0; i < runCount; i++)
{
list += '<li>' + i + '</li>';
}
$('#list').append(list);
}
function testAppendAllUlLi()
{
var list = '';
for (var i = 0; i < runCount; i++)
{
list += '<li>' + i + '</li>';
}
$('#cnt').append('<ol id="list">' + list + '</ol>');
}
Тест testAppendLiCacheSelection попал на этот график с той лишь целью, чтобы показать
эффект от кеширования (пусть даже очень быстрого селектора #id).
Собирайте в один элемент
Чтобы ускорить вставку большого количества элементов, их нужно обернуть в один элемент.
Обратите внимание, что тест testAppendAllUlLi работает быстрее, чем тест на чистом DOM и практически так же быстро как innerHTML, то же самое касается методов
html, after и других методов с таким функционалом.
Значительное ускорение при обертывании кода в один элемент связано со способом которым
jQuery внедряет html код в документ. После предварительной обработки кода, создается
элемент div и код присваивается его свойству innerHTML, потом поэлементно клонируется
в документ, обертывание в кода в один элемент позволяет минимизировать клонирования.
Функция $.each
Не используйте $.each там где важна скорость работы с массивом. $.each — это
вызов функции в контексте объекта и последующая проверка необходимости досрочного
выхода из перебора (зависит от возвращаемого функцией значения), очевидно, что простой
for без лишних вызвов функций и проверок значительно быстрее $.each. Результаты
тестов это подтверждают:
Разница в производительности $.each и for in применительно к объектам не столь значительна,
вероятно это связано с тем, что for in не столь быстр как for и дополнительные расходы
на вызов функции и if на его фоне не столь заметны.
Не используйте for in для массивов
Ну и еще раз подтверждение известного факта о медлительности цикла for in в применении
к массивам:
Конкатенация строк
Конкатенация не относится к jQuery, но это одна из часто используемых операций, причем
с подводным камнем, она фантастически медленно выполняется в IE6 и IE7.
Если быстрая конкатенация нужна здесь и сейчас, можно использовать Array.push(string)
и Array.join(''), но использовать их повсеместно не рекомендуется, так как конкатенация
хорошо поддается оптимизации и быстрее join почти во всех современных браузерах
(а если не быстрее, значит разработчики использовали не все возможности для ее оптимизации
и она может ускориться в будущем).
Тот же самый график без IE6 и IE7:
Код тестов:
function testConcatenationOperator()
{
var str = '';
for (var i = 0; i
Проблема конкатенации в IE6/7 в том, что она выполняется тривиально, для каждых двух
операндов создается буфер в который они последовательно копируются, рассмотрим пример:
var s = 'str1' + 'str12' + 'str123'
В IE6/7 при выполнении первой конкатенации выделяется память под временную строку
и в нее копируются строки 'str1' и 'str12', при выполнении второй конкатенации выделяется
память под еще одну строку и в нее копируется строки 'str1str12' и 'str123', и т.д.
При каждой конкатенации происходит выделение памяти и копирование строк.
В эффективной реализации при выполнении первой конкатенация создается вспомогательный
объект, содержащий ссылки на строки 'str1' и 'str12', при выполнении второй конкатенации
добавляется ссылка на 'str123' и только когда потребуется значение строки, выделяется
память в которую копируются строки 'str1', 'str12', 'str123'.
Подробнее об этом можно почитать в статьях:
Performance issues with "String Concatenation" in JScript
Insight into String Concatenation in JScript
Ускорение доступа к переменным
Еще один прием которому можно научиться у jQuery это ускорение глобальных переменных
созданием соответствующей локальной.
Метод прост, создаются локальная переменная, равная соответствующей глобальной, а доступ к локальным переменным во многих браузерах быстрее доступа к глобальным переменным,
иногда более чем в 10 раз, в худшем случае разницы нет.
Код теста:
var runCount = 1000000;
var globalVariable = 1;
function testGlobalVariable()
{
for (var i = 0; i
Пример использования оптимизации undefined в приближенных к реальным условиях:
function testArrayLocalUndefined()
{
var array = globalArray; //массив в котором каждый 5-й элемент определен
var summ = 0;
var undefined; //ускоритель
for (var i = 0, l = array.length; i
jQuery использует эту технику для ускорения window и undefined.
Статьи
- Improve your jQuery - 25 excellent tips
- jQuery Performance Rules
- 10 Ways to Instantly Increase Your jQuery Performance
- Ускоряем селекторы в jQuery
- Закрепляем jQuery — 25 отличных советов
- jQuery Internals
Оставить комментарий
Комментарии





