Разгоняем 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
Оставить комментарий
Оставлять комментарии могут только зарегистрированные пользователи.
Если вы не являетесь зарегистрированным пользователем, то вам необходимо зарегистрироваться. Регистрация бесплатна. Если вы уже зарегистрированы на CodeNet, то вам необходимо ввести логин и пароль в верхней (Alt-U) части страницы.
