Вы находитесь на странице: 1из 58

Низкоуровневая оптимизация

JavaScript

Федоров Александр Юрьевич


Самое главное в оптимизации

• Оптимальный алгоритм
• Соотвествующие структуры данных
• Не увлекатся попустуту
• Изначально писать код средне оптимальным
• Жесткую оптимизацию оставить на самый конец
Как исполняется ecmascript

Код Байт-код/ассемблер
while(++a); label: inc eax
jnz label
Цепь скоупов

Global === window

_fn0

_fn1

_fnn
Ресолвинг переменных

глобальная область ближайшее замыканиелокальные


Opera 10.52 5742 310 52
firefox 3.6 10955 1917 644
ie 6410 3210 1200
chrome4 10198 78 59
Кэшируем глобальные переменные

(function(window)
{
var document = window.document,
documentElement = document.documentElement
;

})(window);
Приватный кеш для функции

(function()
{
var cachedVar = 1,
_cmp = function(a, b){ return a < b; },
_min = Math.min;

Class.prototype._fn = function(a, b)
{
this.array.sort(_cmp);

return _min(a, b + cachedVar);


};
})();
Аналог в JavaScript 1.7

<script type="application/javascript;version=1.7"/>
let (
cachedVar = 1,
_cmp = function(a, b){ return a < b; },
_min = Math.min
)
{
Class.prototype._fn = function(a, b) {
this.array.sort(_cmp);

return _min(a, b);


};
};
Быстрый ресловинг функции самой себя

function() { function _self() {


arguments.callee.a = 1; _self.a = 1;
} }
Opera 10.53 1673 1068
firefox 3.6 2477 767
ie 9454 NaN
Пример использования быстрого ресолвинга
функции самой себя
if(Function.canFastSelf) {
$jb._fConstC.__fRet = function() {
return function _self() {
return _self.c;
};
};
} else {
$jb._fConstC.__fRet = function() {
return function() {
return arguments.callee.c;
};
};
}
Создание Объекта

a = {} a = new Object()
Opera 10.52 906 1237
firefox 3.6 1319 1888
ie 3154 4016
chrome4 1218 1891
Создание Массива

A = [] a = new Array()
Opera 10.52 1144 1627
firefox 3.6 1234 1875
ie 3214 4136
chrome4 1284 1938
Меряем «тики» или сравнение сколько стоит
создание объекта/функции

{} = n*'' function(){} = n*''


Opera 10.53 2,4 3
firefox 3.6 2,75 2,75
ie 6,5 6,25
chrome4 1,9 2,5
If() и switch()

switch(a) { if(a === 0) {


case 0: break; } else if(a === 1) {
case 1: break; }
}
Opera 10.52 1089 1575
firefox 3.6 1171 1832
ie 1572 1682
chrome4 1988 2582
Switch switch switch!!!
Function.prototype._fBind = function(that, args) {
var _fn = this, _ret;
if(that != null) {
_ret = (args != null)
? function(){ return (arguments.length > 0) ? _fn.apply(that,
arguments) : _fn.call(that); };
: function(){ return _fn.apply(that, args); }
} else {
_ret = (args != null)
? function(){ return (arguments.length > 0) ? _fn.apply(this,
arguments) : _fn.call(this); };
: function(){ return _fn.apply(this, args); };
}
_ret.prototype = _fn.prototype;
return _ret;
};
Switch switch switch!!!
Function.prototype._fBind = function(that, args) {
var _fn = this, _ret;
switch((that != null) + ((args != null) << 1)) {
case 3: _ret = function(){ return _fn.apply(that, args); };
break;
case 1: _ret = function(){ return (arguments.length > 0) ?
_fn.apply(that, arguments) : _fn.call(that); };
break;
case 2: _ret = function(){ return _fn.apply(this, args); };
break;
default: _ret = function(){ return (arguments.length > 0) ?
_fn.apply(this, arguments) : _fn.call(this); };
}
_ret.prototype = _fn.prototype;
return _ret;
};
Switch switch switch again!!!

switch(mod8) {
case 7: a[--j] = this[--i];
case 6: a[--j] = this[--i];
case 5: a[--j] = this[--i];
case 4: a[--j] = this[--i];
case 2: a[--j] = this[--i];
case 3: a[--j] = this[--i];
case 1: a[--j] = this[--i];
case 1: a[--j] = this[--i];
case 0: a[--j] = this[--i];
}
Лисп ^_^ или сокращаем ресолвинг

f.style.left = ((d.x = d.r*_sin(d.t += d.dt) + d.x0)|0) - 20 +


'px';

f.style.left = ((d.x = d.x0 = _random()*($A.wWidth - 40) + 20)|0)


+ 'px';

if((temp = d.x - x)*temp + (temp = d.y - y)*temp < 2601)

(
(
this.prototype = Object.create(_constuctor.prototype)
). constructor = this
).superClass = _constuctor;
Цыклы

for(j=0; j< m ; j+ +)j= -1; while(++ j<mfor(j=


) m ; j> = 0; j—j=
) m ; while(j--)
a = j; a = j; a = j; a = j;
Opera 10.52 4958 4920 4013 3763
firefox 3.6 5955 5083 5056 4193
ie 6492 4626 5106 3606
chrom e4 10723 8756 9112 7137
Преобразования объектов/примитивов в булево
значение

0 Ч ис ло != 0пу стая с троканепу с кая с трокаo bjec t NaN Infinity null undefined
fals e true fals e true true false true fals e fals e
Обход цельных массивов

i = vs.length; while((v = vs[--i]))


;
i = -1; while((v = vs[++i]))
;
Цепь прототипов

Object.prototype

A.prototype

B.prototype

Z.prototype
Ресолвинг свойств

Из объекта из прототипа объекта


Opera 10.52 6371 6361
firefox 3.6 7105 7361
ie 7779 8595
chrome4 10041 10059
Хранение свойств в DOM и в Object

(v = a[j]).x = 1; v.y = 1; v.z = v.x;(v = a[j].b).x = 1; v.y = 1; v.z = v.x;


v.g = v.x + v.y + v.z; v.g = v.x + v.y + v.z;
Opera 10.52 3278 3385
firefox 3.6 5669 5246
ie 1572 1682
chrome4 4758 2292
Нооптимальный ООП

var Class = function(a0, a1)


Class.prototype
{
this.publicVar = defaultValue;

this._fn0 = function()
{
this.publicVar = 1; Class closure •classInst
a0 .publicVar
};
a1 ._fn0
};

var classInst = new Class(1, 2);


classInst._fn0();
Оптимальный ООП

var Class = function(a0, a1)


Class.prototype
{
._fn0
this.publicVar = defaultValue;
};
Class.prototype._fn0 = function()
{
this.publicVar = 1; •classInst
}; .publicVar

var classInst = new Class(1, 2);


classInst._fn0();
Статическое наследование

Function.prototype._staticDeriveFrom = function(_constuctor)
{
var key, dpr = this.prototype, spr = _constuctor.prototype;

for(key in spr)
{
if(spr.hasOwnProperty(key) && !(key in dpr))
dpr[key] = spr[key];
}

return this;
};
Пример статического наследования

var DerivedClass = function(a0, a1) { DerivedClass.prototype


Class.call(this, a0, a1); ._fn0
}; ._fn1
DerivedClass._staticDeriveFrom(Class);

DerivedClass.prototype._fn1 = function() {
•classInst
Class.prototype._fn0.call(this); .publicVar
};

var derivedClassInst = new DerivedClass(1, 2);


derivedClassInst._fn1();
Статический биндинг

var Class = function(a0, a1)


{ Class.prototype
this._onMouseMove = .onMouseMove
this._onMouseMove.bind(this);
};
Class.prototype._onMouseMove = function()
{
•classInst
};
.onMouseMove binded

var classInst = new Class(1, 2);


document.onmousemove = classInst._onMouseMove;
Почему for in плох для обхода массивов

for(j in a) {
j = a.length; while(j--) if(a.hasOwnProperty(j))
++k; ++k;
}
Opera 10.53 1181 2162
firefox 3.6 1418 2073
ie 1212 4998
chrome4 1925 2780
Ресолвинг функции с вызовом против
кеширования и вызова через .call

a = []; a = []
a._fn('b') _fn.call(a, 'b')
Opera 10.53 1236 1546
firefox 3.6 1641 2436
ie 5247 6549
chrome4 1476 1834
Проверка на undefined

typeof(v) !== 'undefined' v !== undefined v != null


Opera 10.52 305 313 273
firefox 3.6 286 431 192
ie 411 361 200
chrome4 461 581 351
Проверка свойства по индексу на undefined
без сохранения значения

a['0'] !== undefined typeof(a['0']) !== 'undefined' '0' in a


Opera 10.52 2320 1911 1863
firefox 3.6 3346 1926 1881
ie 3848 2924 2564
chrome4 5217 3686 3519
Проверка свойства по индексу на undefined с
сохранением значения

(v = a['0']) !== undefined, vtypeof(v = a['0']) !== 'undefined', v('0' in a), a[i]
Opera 10.52 3055 2580 2113
firefox 3.6 3798 2456 1925
ie 3655 3005 4505
chrome4 5109 4216 3628
Вызов функции

• Создание скоупа
• Конструирование arguments *
• jump
.each - зло
Динамическое создание
функции
$('div.a').each( jit
function(v) установка указателя на код функции
{ построение списка замыканий
$(v).removeClass('a');
}
Вызов функции
); ресолвинг функции
создание arguments
// или создание скоупа

for(var nodes = $('div.a'), i = 0, len = nodes.length; i < len; ++i)


$(nodes[i]).removeClass('a');
Вызов функции через .call / .apply

_fn.call(that) _fn.apply(that, args)


Opera 10.52 1544 2037
firefox 3.6 2645 2847
ie 7460 10170
chrome4 1986 2490
isNaN

a !== a _isNaN(a)
Opera 10.52 1175 1175
firefox 3.6 1052 1061
ie 1142 2674
chrome4 1920 1972
isFinite

a/a === 1 || a === 0_isFinite(a)


Opera 10.52 1532 1280
firefox 3.6 1218 1050
ie 1372 2734
chrome4 2026 2164
RegExp ввиде объекта и литерала

s.replace(/\s/g, ''); var re = new RegExp(/\s/);


s.replace(re, '');
Opera 10.52 3838 2054
firefox 3.6 7014 2211
ie 13600 7070
chrome4 7853 2395
Конвертация Строка -> Число

_parseFloat(a) +a 1*a a - 0
Opera 10.52 695 403 464 435
firefox 3.6 531 416 402 391
ie 1212 380 401 391
chrome4 1039 744 791 823
Конвертируем вещественные числа в целые

a|0 a>>0 a&-1 _floor(a)


Opera 10.53 1386 1370 1371 1909
firefox 3.6 1606 1567 1575 1762
ie 1211 1213 1231 4287
chrome4 2652 2501 2544 3313
Конвертация в строку

a.toString() String(a) StringC(a) '' + a


Opera 10.52 478 446 413 281
firefox 3.6 594 1100 961 304
ie 1833 1652 1472 1042
chrome4 633 893 737 566
Объектное представление примитивов

• '1'.slice() <=> Object.('1').slice()


• Вызов стандартных методов не
конвертирует примитив в объект

Сравнение строки со множеством строк.
RegExp.test или switch

re = new RegExp(/^link|tbody$/);
re.test(a) case 'link': case 'tbody':
Opera 10.52 2161 1723
firefox 3.6 3167 1726
ie 7572 2402
chrome4 4548 3118
String char access

P.charAt O.charAt P.substr O.substr P.slice O.slice P[] O[] split[]


Opera 10.52 1403 1336 1344 1373 1350 1401 1084 1059 1069
firefox 3.6 1871 1967 2161 2345 2238 2399 1014 1139 1015
ie 7510 10810 7720 11310 7710 11120 n/s n/s 1400
chrome4 2210 2439 2262 2473 2250 2427 2189 2324 1986

if(String.nativeDirectCharAccess) {
_fn = new Function(
_fn._argsString(),
_fn._body().slice(1, -1).replace(/\.\s*charAt\s*\
(([^\)]*)\)/g, '[$1]');
}
.slice у строк не копирует (кроме ie)

s.slice(0, 8000); s.slice(0, 5000);


Opera 10.53 388 345
firefox 3.6 510 528
ie 5818 4256
chrome4 557 594
Трюки для работы с поиском в строке

var p = ~(~s.indexOf('a') || ~s.indexOf('b') || ~s.indexOf('c') ||


~oldP);
var p = s.lastIndexOf('a') >>> 0;

// fix for opera


code.replace(
/\s(.*?)\s*=(.*?)>>>\s*0/g,
'if(($1 = $2) < 0) $1 = 4294967295;'
);
Бинарные Алгоритмы
Повторение строки
String.prototype.__mulNumber = function(n, prefix) {
n |= 0;
prefix += "";

var str = "" + this;

while(n) {
if(n&1) prefix += str;

n >>= 1;
str += str;
}
return prefix;
};
Бинарные алгоритмы
var failCount = -1;
while(p < sLen) {
if(s.substr(p, matchMultyLen) === matchMulty) {
p += matchMultyLen; matchMultyLen <<= 1; matchMulty += matchMulty;
} else {
if(matchMultyLen === matchLen) break;

if(++failCount < 2) {
matchMulty = matchMulty.substr(0, (matchMultyLen >>= 1));
} else {
matchMultyLen = matchLen;
matchMulty = match;
failCount = -1;
}
}
}
Посткомпиляция

var s = $h.getElementsByTagName('script')[0], checkerBody = '';

if('readyState' in s) {
checkerBody +=
"var rs = v.readyState; \
if(rs === 'loaded' || rs === 'complete')\
return true; \
else\
return false;";
}
if($w.opera) {
checkerBody += "if(v.text != null) return true;";
}
checkerBody += "return null;";
Карты оптимизации

String.prototype.trim = function()
{
return (this.lenght < 100) ? this.__trim1() : this.__trim2();
};

String.prototype.trim = ($w.opera) ?
function(){...}
: function(){...};
Карты оптимизации
0 100 2500
arg0
_fn0 _fn1 _fn2 +inf

0,0 500 arg0

_fn2 _fn0
1000

_fn3 _fn1
arg1
Карты оптимизации
• Реализации ввиде отдельных функций
• Хранение ввиде json/xml
• Debug = Шаблозирование/препроцессинг на клиенте без
инлайна
• Release = Предварительное шаблонизирование под конкретный
браузер на сервере. Остатики на клиенте.
• Автоматическая генерация
• Кеширование предрассчитанных карт
• Генерация на клиенте с его согласия*
$jb.Prepocessor

(new $jb.Prepocessor).
_define('_IS_FINITE($V)',
$jb._preprocessingTextBegin(function(){
($V&$V) !== 0 || $V === 0
})._preprocessingTextEnd()
);
General PreProcessor

#define _IS_FINITE($V)
($V&$V) !== 0 || $V === 0
#end
Контакты

• Twitter: https://twitter.com/bga_
• Email: mailto:bga.email@gmail.com
• jabber/gtalk: bga.email@gmail.com
Сюда пишется заголовок слайда, также до 3
строк, не больше!

• Это – контентная область слайда.


• Желательно создавать новые слайды, дублируя (copy/paste)
этот слайд в качестве образца, чтобы сохранить общую
стилистику презентации. Также рекомендуется для набора
текста использовать шрифт Trebuchet.