Примечание: ниже перевод презентации "High Performance Ajax Applications", подготовленной ведущим специалистом из Yahoo (а теперь уже из Apple) Julien Lecomte. В ней автор освещает некоторые аспекты оптимизации как JavaScript-приложений, так и веб-сайтов вообще. В целом, советов много, и почти все, действительно, по делу. Однако, встречается и откровенная реклама Yahoo :) Мои комментарии далее курсивом.
Веб-страница работает в 3 иногда перекрывающихся состояниях:
Следующие правила покрывают, в основном, первое состояние.
Для более подробной информации можно ознакомиться с переводом этой статьи на webo.in.
<head>.onload...onload.</body>.onDOMReady в утилите YUI Event:YAHOO.util.Event.onDOMReady(function () {
// Выполняем какие-нибудь действия...
// например, прикрепляем обработчики событий.
});</body> добавьте следующее:<script>
window.onload = function () {
var script = document.createElement("script");
script.src = ...;
document.body.appendChild(script);
};
</script>var g = 7;
function f(a) {
var v = 8;
x = v + a + g;
}
f(6);var) и избегайте использования глобальных переменных любой ценой.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;
})();function A () {}
A.prototype.prop1 = ...;
function B () {
this.prop2 = ...;
} B.prototype = new A();
var b = new B();// быстрее для создания большого количества одинаковых объектов
function Foo () {...}
Foo.prototype.bar = function () {...};// быстрее для обращения к свойствам объектов
function Foo () {
this.bar = function () {...};
}eval!eval (и всем родственным ему методам: конструктору Function, функциям setTimeout и setInterval), должна быть скомилирована и выполнена. Это очень медленно!setTimeout и setInterval. Вместо это используйте анонимную функция, например:setTimeout(function () {
// код, который нужно выполнить с задержкой
}, 50);eval и конструктор Function (кроме, может быть, некоторых очень редких случаев, и только в тех блоках, где производительность не является критичной, т.е. расширяемость или логичность модели, например, будут важнее, чем производительность).var s = "xxx" + "yyy"; s += "zzz";
Array.join, объединить (не используйте это для простых объединений, работает сильно медленее!)// медленнее
var i, s = "";
for (i = 0; i < 10000; i++) {
s += "x";
}// быстрее
var i, s = [];
for (i = 0; i < 10000; i++) {
s[i] = "x";
}
s = s.join("");realloc + memcpy во всех возможных случаях объединения строк.RegExp, если вы не собираетесь создавать регулярное выражение «на лету». Вместо этого используйте постоянные регулярные выражения (regular expression literals).test, если вам нужно просто проверить, соответствует ли строка шаблону (метод exec немного более ресурсоемкий)if (/loaded|complete/.test(document.readyState)) {...}?:...)(?:(?:\r\n)?[\t])*(?:(?:(?:[^()<>@,;:\\".\[\]\000-\031]+ (?:(?:(?:\r\n)?[\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?: [^\"\r\\]|\\.|(?:(?:\r\n)?[\t]))*"(?:(?:\r\n)?[\t])*)(?: \.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\".\[\]\000-\031]+(?:( ?:(?:\r\n)?[\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\" \r\\]|\\.|(?:(?:\r\n)?[\t]))*"(?:(?:\r\n)?[\t])*))*@(?:( ?:\r\n)?[\t])*(?:[^()<>@,;:\\".\[\]\000-\031]+(?:(?:(?:\ r\n)?[\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\] |\\.)*\](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^( )<>@,;:\\".\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\ ["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)? [\t])*))*|(?:[^()<>@,;:\\".\[\]\000-\031]+(?:(?:(?:\r\n) ?[\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.| (?:(?:\r\n)?[\t]))*"(?:(?:\r\n)?[\t])*)*\<(?:(?:\r\n)?[\ t])*(?:@(?:[^()<>@,;:\\".\[\]\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)* \](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,; :\\".\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\["()<> @,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])* ))*(?:,@(?:(?:\r\n)?[\t]))
Модульный шаблон
var fn = (function () {
var b = false, v;
return function () {
if (!b) {
v = ...;
b = true;
}
return v;
};
})();Сохранение значение в объекте функции
function fn () {
if (!fn.b) {
fn.v = ...;
fn.b = true;
}
return fn.v;
}«Ленивое» объявление функции
var fn = function () {
var v = ...;
return (fn = function () {
return v;
})();
};setTimeout.function doSomething (callbackFn) {
// Здесь инициализируем данные...
(function () {
// Делаем некоторую работу...
if (termination condition) {
// мы закончили
callbackFn();
} else {
// обрабатываем следующую порцию
setTimeout(arguments.callee, 0);
}
})();
}var a = 1, b = 2, c; // медленнее c = Math.min(a, b); // быстрее c = a < b ? a : b;
// медленнее myArray.push(value); // быстрее myArray[myArray.length] = value; // еще быстрее myArray[idx++] = value;
try...catch в тех частях, в которых критична производительность:// медленнее
var i;
for (i = 0; i < 100000; i++) {
try {
...
} catch (e) {
...
}
}// быстрее
var i;
try {
for (i = 0; i < 100000; i++) {
...
}
} catch (e) {
...
}for...in в тех частях, в которых критична производительность:// медленнее
var key, value;
for (key in myArray) {
value = myArray[key];
...
}// быстрее
var i, value, length = myArray.length;
for (i = 0; i < length; i++) {
value = myArray[i];
...
}// медленнее
function fn () {
if (...) {
...
} else {
...
}
}// быстрее
var fn;
if (...) {
fn = function () {...};
} else {
fn = function () {...};
}innerHTMLvar i, j, el, table, tbody, row, cell;
el = document.createElement("div");
document.body.appendChild(el);
table = document.createElement("table");
el.appendChild(table);
tbody = document.createElement("tbody");
table.appendChild(tbody);
for (i = 0; i < 1000; i++) {
row = document.createElement("tr");
for (j = 0; j < 5; j++) {
cell = document.createElement("td");
row.appendChild(cell);
}
tbody.appendChild(row);
}(сильно быстрее во всех браузерах класса А)
var i, j, el, idx, html;
idx = 0;
html = [];
html[idx++] = "<table>";
for (i = 0; i < 1000; i++) {
html[idx++] = "<tr>";
for (j = 0; j < 5; j++) {
html[idx++] = "<td></td>";
}
html[idx++] = "</tr>";
}
html[idx++] = "</table>";
el = document.createElement("div");
document.body.appendChild(el);
el.innerHTML = html.join("");Замечание: прочитайте http://www.julienlecomte.net/blog/2007/12/38/ (перевод)
cloneNodevar i, j, el, table, tbody, row, cell;
el = document.createElement("div");
document.body.appendChild(el);
table = document.createElement("table");
el.appendChild(table);
tbody = document.createElement("tbody");
table.appendChild(tbody);
for (i = 0; i < 1000; i++) {
row = document.createElement("tr");
for (j = 0; j < 5; j++) {
cell = document.createElement("td");
row.appendChild(cell);
}
tbody.appendChild(row);
}(быстрее по всех браузерах класса А, иногда значительно быстрее)
var i, el, table, tbody, template, row, cell;
el = document.createElement("div");
document.body.appendChild(el);
table = document.createElement("table");
el.appendChild(table);
tbody = document.createElement("tbody");
table.appendChild(tbody);
template = document.createElement("tr");
for (i = 0; i < 5; i++) {
cell = document.createElement("td");
template.appendChild(cell);
}
for (i = 0; i < 1000; i++) {
row = template.cloneNode(true);
tbody.appendChild(row);
}Замечание: расширенные свойства (expando properties) и прикрепленные обработчики событий будут утеряны!
DocumentFragmentDocumentFragment (DOM Level 1 Core) является облегченным вариантом Document.DocumentFragment не соответствует спецификации W3C и возвращает обычный объект Document.var i, j, el, table, tbody, row, cell, docFragment;
docFragment = document.createDocumentFragment();
el = document.createElement("div");
docFragment.appendChild(el);
table = document.createElement("table");
el.appendChild(table);
tbody = document.createElement("tbody");
table.appendChild(tbody);
for (i = 0; i < 1000; i++) {
...
}
document.body.appendChild(docFragment);event delegation, технику, базирующуюся на event bubbling<div id="container">
<ul>
<li id="li-1">List Item 1</li>
<li id="li-2">List Item 2</li>
<li id="li-3">List Item 3</li>
<li id="li-4">List Item 4</li>
<li id="li-5">List Item 5</li>
...
</ul>
</div>YAHOO.util.Event.addListener("container", "click", function (e) {
var el = YAHOO.util.Event.getTarget(e);
while (el.id !== "container") {
if (el.nodeName.toUpperCase() === "LI") {
// Что-нибудь делаем...
break;
} else {
el = el.parentNode;
}
}
});display:none) не вызывают отрисовкуstyle при помощи метода setAttribute (не работает в Internet Explorer). Пример:el.setAttribute("style", "display:block;width:auto;height:100px;...");cssText объекта style. Пример:el.style.cssText = "display:block;width:auto;height:100px;...";
YAHOO.util.Dom.replaceClass(el, "foo", "bar");
onmousedown вместо onclickwindow.onresize...table-layout:fixedCOL для каждой колонки.WIDTH.XMLHttpRequest.var callback = {
success: function () { /* Что-нибудь делаем */ },
failure: function () { /* Что-нибудь делаем */ },
timeout: 5000
};
YAHOO.util.Connect.asyncRequest("GET", url, callback);userData для Internet ExplorerDOM:Storage (стабильное API для хранения данных от WhatWG, реализовано в Firefox 2)Если у вас возникли какие-либо вопросы или пожелания по работе Web Optimizator, пожалуйста, поделитесь ими, используя контактную информацию.
Спасибо за использование сервиса!
This work is licensed under a Creative Commons Attribution 3.0 Unported License.