Россия, Сочи, РГПУ им. А.И.Герцена, 1997 |
Оптимизация JavaScript
Быстрые итераторы, регулярные выражения и другие "вкусности"
В этом разделе собраны некоторые практические советы по производительности отдельных конструкций в 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(); // запускаем первый тест, обычный перебор элементов массива, // запускается 10000 раз for (var j=0; j<10000; j++) { for (var i=0; i<items.length; i++) { var item = items[i]; } } // выводим результат в подготовленный выше контейнер document.getElementById('test1').innerHTML = "Простой цикл: " + (new Date().getTime() - time); time = new Date().getTime(); // кэшируем размер массива for (var j=0; j<10000; j++) { for (var i=0; i<length; i++) { var item = items[i]; } } document.getElementById('test2').innerHTML = "Простой цикл (с кэшированием): " + (new Date().getTime() - time); time = new Date().getTime(); // встроенный for-in итератор для объекта массива for (var j=0; j<10000; j++) { for (var i in items) { var item = items[i]; } } document.getElementById('test3').innerHTML = "Простой через for-in: " + (new Date().getTime() - time); time = new Date().getTime(); // обратный перебор элементов массива for (var j=0; j<10000; j++) { for (var i = length - 1; i >= 0; i--) { var item = items[i]; } } document.getElementById('test4').innerHTML = "Обратный: " + (new Date().getTime() - time); time = new Date().getTime(); // итератор do-while for (var j=0; j<10000; j++) { var i = 0; do { var item = items[i]; i++; } while (i < length) } document.getElementById('test5').innerHTML = "do-while: " + (new Date().getTime() - time); time = new Date().getTime(); // обратный while (самый быстрый) for (var j=0; j<10000; j++) { var i = length - 1; while (--i) { var item = items[i]; } } document.getElementById('test6').innerHTML = "Обратный while: " + (new Date().getTime() - time); </script>
В результате мы получим примерно следующую таблицу:
Браузер | Обычный | С КЭШем | for-in | Обратный | do-while | Обратный while |
---|---|---|---|---|---|---|
Firefox 3.0.3 | 714 | 657 | 835 | 280 | 297 | 217 |
Safari 3.1.2 | 141 | 140 | 157 | 125 | 125 | 93 |
Opera 9.61 | 188 | 125 | 765 | 94 | 94 | 78 |
IE 6 | 1281 | 1219 | 1094 | 468 | 500 | 360 |
IE 7 | 1391 | 1297 | 1250 | 515 | 532 | 406 |
IE 8b2 | 954 | 906 | 922 | 406 | 422 | 328 |
Chrome 0.2 | 288 | 246 | 332 | 117 | 114 | 95 |
В общем случае применение обратного while для перебора цикла в 2-3 раза быстрее всех остальных вариантов. Если веб-приложение оперирует массивами порядка 1000 элементов, то в результате применения оптимизированных приемов будет заметен значительный прирост производительности.
Регулярные выражения
В JavaScript есть несколько способов проверить, удовлетворяет ли строка заданному шаблону:
// 1. Объявляем объект в виде регулярного выражения var RegExp = '/script/gi'; // и ищем в элементе массива совпадение с заданным шаблоном items[i].nodeName.search(RegExp); // 2. можно просто проверять соответствие строке, // а не искать индекс подстроки items[i].nodeName.match(RegExp); // 3. Можно обойтись без объявления самого регулярного выражения items[i].nodeName.match(/script/gi); // 4. Можно задавать регулярное выражение без глобального модификатора, // ведь мы ищем любое (=первое) совпадение шаблона items[i].nodeName.match(/script/i); // 5. С тем же успехом мы можем выполнить шаблон /script/i.exec(items[i].nodeName); // 6. Наконец, можно протестировать сам шаблон на нахождение в строке /script/i.test(items[i].nodeName);
Давайте рассмотрим, что из них работает быстрее всего. Для этого запустим немного модифицированный набор тестов из раздела выше (опять по 10000 раз для всего DOM-дерева). Получим следующие результаты:
Браузер | search | match | "На лету" | Локальный | exec | test |
---|---|---|---|---|---|---|
Firefox 3.0.3 | 2120 | 2041 | 1295 | 1273 | 1225 | 1348 |
Safari 3.1.2 | 453 | 469 | 344 | 359 | 360 | 359 |
Opera 9.61 | 2141 | 2063 | 406 | 344 | 312 | 313 |
IE 6 | 2594 | 2516 | 1875 | 1859 | 1953 | 1906 |
IE 7 | 2562 | 2469 | 1859 | 1844 | 2000 | 1860 |
IE 8b2 | 2140 | 2032 | 1453 | 1453 | 1547 | 1469 |
Chrome 0.2 | 856 | 870 | 416 | 397 | 385 | 392 |
Как мы видим, в данном случае создание нового регулярного выражения - весьма ресурсоемкий процесс, поэтому в большинстве случаев лучше обходиться без него. В остальном все браузеры ведут себя достаточно ровно при сопоставлении match, exec и test.