Оптимизация скорости выполнения скриптов JavaScript.
О том, насколько важна оптимизация скриптов JavaScript, можно судить из сравнения с производительностью других языков программирования. JavaScript приблизительно в 5000 раз медленнее в сравнении с языком C, в 100 раз медленнее Java и в 10 раз медленнее Perl. Ниже представлены несколько простых методик увеличения производительности скриптов:
- Учитывайте область действия переменных.
- Не применяйте with.
- Храните часто употребляемые значения в локальных переменных.
- Уменьшайте количество выражений.
- Использование DOM.
- Используйте join() при конкатенации большого количества значений.
- Использование замыканий (closures).
Учитывайте область действия переменных.
Область действия можно представить себе в виде определенного пространства, в котором существуют переменные. Глобальной областью переменных является объект window. Функции имеют свою, локальную область переменных.
Каждый раз, когда интерпретатор пытается получить значение переменной, он выполняет поиск в локальной области, если переменная не найдена, то выполняется поиск на уровень выше, и так далее, до тех пор пока не будет достигнута глобальная область определения переменных. Следовательно применение локальных переменных увеличивает производительность скриптов. Например:
var vGlobal = "Глобальная переменная";
function func1() {
alert(vGlobal);
}
function func2() {
var vLocal = "Локальная переменная";
func1();
}
function func3() {
var vLocal = "Еще одна локальная переменная";
func2()
}
func3();В приведенном выше примере в последней строке выполняется функция func3, которая вызывает функцию func2, а та в свою очередь функцию func1. Функция func1 пытается получить доступ к глобальной переменной vGlobal, но для того, чтобы найти ее, интерпретатор сразу просматривает область локальных переменных функции func3, затем область функции func2 и т.д. до объекта window. Этот процесс занимает много времени. Следовательно нужно по возможности избегать использования глобальных переменных.
Использование локальных переменных не только увеличивает производительность, но также и снижает расход памяти т.к. они удаляются из памяти сразу после выполнения функций.
Не применяйте with
Удобство использования оператора with имеет свою цену - это создание еще одной области определения переменных. А как мы уже знаем, чем меньше этих областей, тем лучше.
Храните часто употребляемые значения в локальных переменных.
Если вы используете одну и ту же глобальную переменную или свойство глобального объекта более одного раза, сохраняйте его значение в локальной переменной. Пример:
function setStyle(){
div1.style.left = document.body.clientWidth;
div2.style.left = document.body.clientWidth;
div3.style.left = document.body.clientWidth;
}Здесь значение document.body.clientWidth используется трижды, но доступ осуществляется с использованием именованных свойств (значительно более медленная операция по сравнению с доступом к локальным переменным). Этот код можно переписать следующим образом:
function setStyle(){
var width = document.body.clientWidth;
div1.style.left = width;
div2.style.left = width;
div3.style.left = width;
}
Уменьшайте количество выражений
Вполне очевидно, что чем меньше выражений содержатся в коде, тем меньше времени необходимо для его выполнения. Ниже представлены несколько простых способов того, как это сделать:
Определение нескольких переменных
Оператор var можно использовать для определения более чем одной переменной одновременно. Например следующий код использует четыре выражения с оператором var для объявление четырех переменных.
var one = 1; var color = "white"; var someArray = [1,2,3]; var someDate = new Date();
Этот код можно переписать следующим образом:
var one=1, color="white", someArray=[1,2,3], someDate=new Date();
Таким образом мы получаем увеличение производительности за счет удаления трех выражений.
Использование итераторов
Операции инкремента и декремента можно объединять с выражениями, в которых они используются. Например следующий код:
var name = values[i]; i++;
может быть объединен в одно выражение:
var name = values[i++];
Результат выполнения выражений присваивания переменной и инкремента объединены в одно выражение. Обратите внимание на использование постфиксного оперетора инкремента, который увеличивает значение переменной i только после выполнения всего выражения.
Использование литералов при объявлении объектов и массивов
Использование литералов для объявления массивов и объектов не только делает код более быстрым, но также и значительно уменьшает его размер. Рассмотрим это на примерах.
// объявление и инициальзация объекта User
var User = new Object();
User.name = "John";
User.age = "30";
// объявление объекта User используя литералы
var User = { name: "John", age: 30}Использование DOM
Одной из самых медленных операций в JavaScript является манипуляция с DOM объектами. Всякий раз при доавлении, удалении объектов или изменении структуры DOM, происходит перерисовка страницы, что влечет за собой значительный расход системных ресурсов. Эти проблемы можно избежать, если осуществлять все манипуляции с элементами, которые еще не добавлены в DOM документ. Рассмотрим пример:
var ul = document.getElementById("bulleted_list");
for (var i=0; i<10; i++){
var li = document.createElement("li");
ul.appendChild(li);
li.appendChild(document.createTextNode("Item " + i));
}В этом примере создаются 10 элементов li с текстом внутри, которые поочередно добавляются в элемент ul. Проблема состоит в том, что в таком случае структура DOM меняется 20 раз из-за того, что сначала элемент li добавляется в DOM, что приводит к перерисовке страницы, а затем текстовый элемент также добавляется в структуру DOM, что еще раз приводит к перерисовке всей страницы. Чтобы избежать этого нужно просто поменять местами порядок добавления элементов, т.е. добавлять текстовый элемент внутрь элемента li до того, как он добавлен в DOM. В этом случае тело цикла будет иметь вид:
var li = document.createElement("li");
li.appendChild(document.createTextNode("Item " + i));
ul.appendChild(li);Дополнительно можно использовать элемент Fragment для хранения всех созданных элементов, после чего добавлять их в структуру DOM:
var ul = document.getElementById("bulleted_list");
var frag = document.createDocumentFragment();
for (var i=0; i<10; i++){
var li = document.createElement("li");
li.appendChild(document.createTextNode("Item " + i));
frag.appendChild(li);
}
ul.appendChild(frag);После выполнения цикла все созданные элементы добавляются в список используя метод appendChild (который добавляет дочерние элементы фрагмента, но не сам фрагмент). В этом случае будет всего лишь одна перерисовка, так как фрагмент не является частью документа.
Используйте join() при конкатенации большого количества значений.
Если вам необходимо выполнить сразу несколько конкатенаций в одной строке, то лучше использовать массив сторок, объединяемых при помощи метода join(). Пример:
var sq = 20, v = 200;
// Пример 1 - использование конкатенации
var str = "Вычисленная площадь равна " + sq + "м2, а объем соответственно " + v + "м3";
// Пример 2 - использование функции join().
var str2 = ["Вычисленная площадь равна " , sq , "м2, а объем соответственно " , v , "м3"].join("");
В первом примере конкатенация будет выполнена 4 раза, а во втором всего лишь один раз, при вызове функции join().
Использование замыканий (closures)
Очень часто приходится выполнять код в зависимости от того, какой браузер используется для просмотра страницы. В этом случае можно использовать замыкания для определения той функции, которую необходимо использовать. Например, следующий код определяет, какую функцию необходимо использовать для добавления обработчиков событий к элементам DOM:
var addEvent = function(obj, type, fn){
if (obj.attachEvent){
// для Internet Explorer 6
obj.attachEvent("on"+type, fn);
} else if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
} else {
obj["on"+type] = fn;
}
}В приведенном выше коде, при выполнении функции addEvent каждый раз будет происходить проверка наличия функций attachEvent или addEventListener. Это можно избежать, используя замыкания:
var addEvent = (function(){
if (document.addEventListener){
return function(obj, type, fn){
obj.addEventListener(type, fn, false);
}
} else if (document.attachEvent){
// для Internet Explorer
return function(obj, type, fn){
obj.attachEvent("on"+type, fn);
}
} else {
return function(obj, type, fn){
obj["on"+type] = fn;
}
}
})();После такого преобразования проверка на наличие функций addEventListener и attachEvent будет произведена только один раз, при инициализации функции addEvent, которая в результате будет выглядеть так (на Firefox):
function addEvent(obj, type, fn){
obj.addEventListener(type, fn, false);
}пластиковые окна 6-й Останкинский переулок. авто аренда микроавтобуса, аренда авто, автомобиль с водителем трансфер
25 марта 2008 в 1:04
[…] предыдущей статье, посвященной оптимизации скриптов (Оптимизация скорости выполнения скриптов JavaScript) я описал 7 методов ускорения работы скриптов. Однако […]
25 марта 2008 в 13:33
Всё-таки не “закрывания”, а “замыкания”…
25 марта 2008 в 13:46
Спасибо, учту
24 мая 2008 в 11:05
Чет трекбек к вам не прислался(
24 мая 2008 в 11:22
Странно, попробую разобраться