вторник, 29 апреля 2008 г.

Зачем нам with ?

Иногда возникает соблазн поюзать with для изменения свойств обьекта. Удобно, красиво, но если протестировать, этот момент полуачается довольно ресурсоемким.
// Например, так лучше не делать !

with(obj.param){
height = 10;
width = 10;
}

// Проще и быстрее

var param = obj.param;
param.height = 10;
param.width = 10;

// Для IE разницы почти нету, а вот в FF прирост приличный !


вторник, 22 апреля 2008 г.

Оптимизируем getElementById

Данный метод пригодится для ajax приложений, где часто приходится обращаться к элементам по айди, оптимизировать можно кэшируя элементы, что приводит к серьезному приросту.


// Всё очень просто.
// В результате приблизительно +300% производительности.
var cache = [];
function $(id){
return !cache[id]?cache[id] =
document.getElementById(id):cache[id];
}

// немного модифицировал.
// ибо глобальные переменные часто приносят неприятности.
function $(id){
return !$.cache[id]?$.cache[id] =
document.getElementById(id):$.cache[id];
}
$.cache = [];

понедельник, 24 марта 2008 г.

Drag and Drop

Drag and Drop, при реализации D&D возникает кроссброузерная проблема с событиями, наша задача оптимизировать код, значит использовать как можно меньше условий.
В javascrpit к методам и свойствам обьекта можно обращаться как к ассоциативным массивам, тоесть запись window['alert']('Hello'); будет корректна, значит мы можем сделать предварительную проверку и установку нужных переменных в какойто обьект, затем использовать уже установленные корректные методы и свойства, избегая проверки на совместимость с браузерами.

Следуя всему этому написал такой код:

<script>

(function(){
// Браузер
var browser = {ie:false,opera:false,mozilla:false,safari:false};
browser[/ie/.test(navigator.userAgent.toLowerCase())?'ie':
/mozilla/.test(navigator.userAgent.toLowerCase())?'mozilla':
/opera/.test(navigator.userAgent.toLowerCase())?'opera':''
] = true;

// Назначение имён для свойств обьектов.
var ev = {}
ev.target = browser.ie?'srcElement':'target';
ev.pageY = browser.ie?'clientY':'pageY';
ev.pageX = browser.ie?'clientX':'pageX';
window.ev = ev;
})()
var obj = {};

function startDrag(e){
obj = document.getElementById('wnd');
obj.xoffset = e[ev.pageX] - parseInt(obj.style.left);
obj.yoffset = e[ev.pageY] - parseInt(obj.style.top);
document.onmousemove = draging;
document.onmouseup = endDrag;
}

function draging(e){
e = e||window.event
obj.style.top = e[ev.pageY] - obj.yoffset + "px";
obj.style.left = e[ev.pageX] - obj.xoffset + "px";
}

function endDrag(e){
document.onmousemove = null;
document.onmouseup = null;
}

</script>

четверг, 20 марта 2008 г.

Функция trim

Продолжая тему оптимизации работы со строками, можно вспомнить функцию trim и ее решение.

function trim (str){
return str.replace(/(^\s+)|(\s+$)/g, "");
}

Все чисто и понятно, но не так быстро как могло бы быть.
Результаты удаления пробелов из строки в 25000 символов.
IE6-IE7: 16ms;
FF: 16ms;
Opera: 15ms;
Как бы и немного, но можно гораздо быстрее

Как бы и немного, но можно гораздо быстрее.
function trim (str){
str = str.replace(/^\s\s*/, '');
var length = str.length;
while(/\s/.test(str.charAt(--length)));
return str.substring(0, length+1);
}

IE6-IE7: 0ms;
FF: 0ms;
Opera: 0ms;
Такой результат вполне понятен, скрипт не перебирает всю строку, а сперва добирается до порвого символа не пробел, затем с конца идёт цикл пока пробел.

понедельник, 17 марта 2008 г.

Оптимизация JavaScript (String IE)

В этот раз оптимизация касается «любимого» всеми браузера IE. По какой-то причине конкатенация строк в ослике тотально медленная, поэтому приходится мудрить.
Кто сталкивался с динамическим обновлением контента (ajax), эта функция в той или иной конфигурации очень знакома.



function createList(){
var el = document.getElementById('list');
var html = '<ul>';
for (var i = 1; i <= 1000; i++) {
html += '<li>Element';
html += i;
html += '</li>';
}
html += '</ul>';
el.innerHTML = html;

}

Итак посмотрим на время выполнения скрипта.

Mozilla: 31ms;
Opera: 16ms (как всегда радует);
IE6: 230ms (никуда не годится…);
IE7: 67ms (немного лучше);

Выход из этой ситуации есть, он довольно прост и неожидан.

function createList(){
var html = [];
html.push('<ul>');
for (var i = 1; i <= 1000; i++) {
html.push('<li>Element');
html.push(i);
html.push('</li>');
}
html.push('<ul>');
var el = document.getElementById('list');
el.innerHTML = html.join('');
}


Mozilla: 47ms (тут пришлось немного пожертвовать скоростью);
Opera: 16ms ;
IE6: 31ms (другое дело);
IE7: 32ms (и тут получше);

Как, я уже писал никогда не пишите … el.innerHTML += '<li>' … даже если операций совсем немного, присваивайте к innerHTML уже готовый результат.

суббота, 2 февраля 2008 г.

Оптимизируем работу с DOM

Работа с DOM элементами занимает слишком много времени, поэтому обращаться к свойствам следует как можно меньше.
Простой пример создания информационного блока, используя innerHTML:




function createHTML(block){
block.innerHTML = '';
block.innerHTML += createTitle();
block.innerHTML += createBody();
}

Не стоит оброщатся к элементу несколько раз, если можно обойтись одним:

function createHTML(block){
var temp = '';
temp += createTitle();
temp += createBody();
block.innerHTML = temp;
}

Кстати говоря, тоже самое относится и к document.write:

//Никогда так не делайте
document.write('текст');
document.write(txt);
document.write('текст');
//Пишите так:
var str = '';
str = 'текст'+txt+'текст';
document.write(str);

Это довольно распространенная ошибка. Всегда применяйте уже сформированную строку, что намного быстрее чем каждый раз использовать dom.
Чуть позже напишу как можно поправить невыносимо медленную конкатенацию строк в IE....

воскресенье, 27 января 2008 г.

Оптимизация JavaScript (addEvent)

Всем известная функция addEvent и ее стандартная реализация:


function addEvent(obj,name,func){
if(obj.addEventListener) {
obj.addEventListener(name, func, false);
}
else if (obj.attachEvent) {
obj.attachEvent('on'+name, func);
}
else {throw 'Error'};
}

Чтобы избежать постоянной проверки, следует проверять наличие нужных методов непосредственно при создании функции:
 
var addEvent= (function(){
if(document.addEventListener) {
return function(obj,name,func){
obj.addEventListener(name, func, false);
}
}
else if (document.attachEvent) {
return function(obj,name,func){
obj.attachEvent('on'+name, func);
}
}else return function(){throw 'Error'}
})()

Тот же способ можно применить и к detachEvent, removeEventListener.



пятница, 25 января 2008 г.

Оптимизация JavaScript (Array)

Добавление и извлечение последнего элемента массива.

Обычный способ:

var arr = new Array();
arr.push('a');
arr.pop();
//Быстрый способ:
var arr = [];
arr[arr.length]='a';
delete arr[arr.length-1]; // а вот так делать стоит,
ибо тогда arr.length == 1 и arr[0] = 'undefined' используйте arr.pop();
Спасибо Сергею, указал на ошибку :)





четверг, 24 января 2008 г.

Оптимизация JavaScript (Looping)

Чаще всего драгоценное время теряется в циклах, причина на то неправильно построенное условие.
Всегда нужно помнить, что проверка условия срабатывает при каждой итерации, и каждый раз вызывается тот метод или чаще всего свойство объекта, которое запросто можно было бы присвоить переменной.


Обычная выборка из массива:

var a = [1,2,3,4,...]
for(var i=0;i<a.length;i++){
...
}
//Повысить скорость можно так:
var a = [1,2,3,4,...]
for(var i=0,l = a.length;i<l;i++){
...
}


Иногда можно просто изменить конструкцию, например функция in_array:

for(var i=0;i<arr.length;i++){
if(arr[i] === search)return true;
}return false;
//неплохой прирост скорости:
var i = arr.length;
while(i--){
if(arr[i] === search)return true;
}
return false;

Когда дело доходит до объектов или дом, правильная организация цикла может увеличить скорость в разы.

В данном примере вызывается метод вложенного объекта:

for(var i=0;i<100;i++){
a.b.c.d(i);
}
//Сработает гораздо быстрее, если присвоит ссылку метода к переменной.
var d = a.b.c.d;
for(var i=0;i<100;i++){
d(i);
}

Более наглядный пример с формой:

var form = document.forms[0]
for(var i=0;i<form.radio.length;i++){
if(form.radio[i].checked)return form.radio[i].value;
}
//так шустрее:
var form = document.forms[0];
var radio = form.radio;
var l = radio.length;
for(var i=0;i<l;i++){
if(radio[i].checked)return radio[i].value;
}