Производительность яваскрипта

Создание объектов

Первый способ - классический. Мы определяем конструктор, в котором задаём внутреннее состояние объекта, а в прототип подмешиваем методы.

(Classic= function( val ){ this._state= Number( val ); }).prototype= function(){ this.value= function(){ return this._state; }; this.increase= function(){ ++this._state; return this; }; this.decrease= function(){ --this._state; return this; }; this.reset= function(){ this._state= 0; return this; }; return this; }.apply( {} ); return aClassic= new Classic( 0 );

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

BlackBox= function( val ){ var state= Number( val ); var value= this.value= function(){ return state; }; var increase= this.increase= function(){ ++state; return this; }; var decrease= this.decrease= function(){ --state; return this; }; var reset= this.reset= function(){ state= 0; return this; }; return this; }; return aBlackBox= new BlackBox( 0 );

Третий метод используется обычно когда экземпляр должен быть функцией.

Functor= function( val ){ var state= Number( val ); var functor= function(){ return state; }; var value= functor.value= functor; var increase= functor.increase= function(){ ++state; return this; }; var decrease= functor.decrease= function(){ --state; return this; }; var reset= functor.reset= function(){ state= 0; return this; }; return functor; }; return aFunctor= Functor();

Наследование в классичеком стиле осуществляется через задницу, то есть через специального медиума.

var Medium= function(){}; Medium.prototype= Classic.prototype; (ClassicChild= function( val, title ){ Classic.call( this, val ); this._name= title }).prototype= function(){ this.one= function(){ return this.increase(); }; this.two= function(){ return this.one().decrease(); }; this.three= function(){ return this.two().increase(); }; return this; }.call( new Medium ); return aClassicChild= new ClassicChild( 0, 'child' );

Наследование в случае чёрных ящиков более лаконичное.

(BlackBoxChild= function( val, title ){ BlackBox.call( this, val ); var name= title; var increase= this.increase; var decrease= this.decrease; var one= this.one= function(){ increase(); return this; }; var two= this.two= function(){ one(); decrease(); return this; }; var three= this.three= function(){ two(); increase(); return this; }; return this; }).prototype= BlackBox.prototype; return aBlackBoxChild= new BlackBoxChild( 0, 'child' );

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

FunctorChild= function( val, title ){ var functor= Functor( val ); var functorChild= function(){ return functor.apply( this, arguments ); }; for( var key in functor ){ functorChild[ key ]= functor[ key ]; }; var name= title; var increase= functor.increase; var decrease= functor.decrease; var one= functorChild.one= function(){ increase(); return this; }; var two= functorChild.two= function(){ one(); decrease(); return this; }; var three= functorChild.three= function(){ two(); increase(); return this; }; return functorChild; }; return aFunctorChild= new FunctorChild( 0, 'child' );

Дополнительно создадим и внуков.

var Medium= function(){}; Medium.prototype= ClassicChild.prototype; (ClassicGrandChild= function( val, title ){ ClassicChild.call( this, val, title ); }).prototype= new Medium; return aClassicGrandChild= new ClassicGrandChild( 0, 'grandchild' ); (BlackBoxGrandChild= function( val, title ){ var blackBoxChild= BlackBoxChild.call( this, val, title ); return this; }).prototype= BlackBoxChild.prototype; return aBlackBoxGrandChild= new BlackBoxGrandChild( 0, 'grandchild' ); FunctorGrandChild= function( val, title ){ var functorChild= FunctorChild( val, title ); var functorGrandChild= function(){ return functorChild.apply( this, arguments ); }; for( var key in functorChild ){ functorGrandChild[ key ]= functorChild[ key ]; }; return functorGrandChild; }; return aFunctorGrandChild= new FunctorGrandChild( 0, 'grandchild' );

Время создания экземпляра new Classic( 1 ); new BlackBox( 2 ); Functor( 3 ); new ClassicChild( 1, 'child' ); new BlackBoxChild( 2, 'child' ); FunctorChild( 3, 'child' ); new ClassicGrandChild( 1, 'grandchild' ); new BlackBoxGrandChild( 2, 'grandchild' ); FunctorGrandChild( 3, 'grandchild' );

Время вызова метода предка aClassic.increase(); aBlackBox.increase(); aFunctor.increase(); aClassicChild.increase(); aBlackBoxChild.increase(); aFunctorChild.increase(); aClassicGrandChild.increase(); aBlackBoxGrandChild.increase(); aFunctorGrandChild.increase(); Время вызова цепочки методов aClassicChild.three(); aBlackBoxChild.three(); aFunctorChild.three(); aClassicGrandChild.three(); aBlackBoxGrandChild.three(); aFunctorGrandChild.three();

Продвинутые браузеры хорошо оптимизируют классическую схему. Однако, в самом медленном случае (ие8) увеличение времени создания объекта компенсируется уменьшением времени вызова его метода, хотя в реальных условиях это всё мелочи.

Теперь проверим работоспособность instanceof.

return aClassicChild instanceof Classic return aBlackBoxChild instanceof BlackBox return aFunctorChild instanceof Functor

return !( aClassic instanceof ClassicChild ) return !( aBlackBox instanceof BlackBoxChild ) return !( aFunctor instanceof FunctorChild )

Поведение сего оператора ожидаемо только при классическом наследовании. Всё потому, что остальные реализуют более продвинутую схему наследования - примешивание. Ее преимущества в том, что к одному классу могут быть примешаны сразу несколько классов, а instanceof такое не может распознать впринципе. Кроме того, при классическом наследовании нарушается инкапсуляция и родитель с потомком становятся жёстко связанны своими внутренностями, в то время как в остальных реализациях дети общаются с предками исключительно через публичные интерфейсы и их внутренние состояния изолированы друг от друга.

На всякий случай проверим не ошиблись ли мы где в реализации.

return (new ClassicGrandChild( 5, 'classic' )).value() === 5 return (new ClassicGrandChild( 5, 'classic' )).increase().value() === 6 return (new ClassicGrandChild( 5, 'classic' )).decrease().value() === 4 return (new ClassicGrandChild( 5, 'classic' )).reset().value() === 0 return (new ClassicGrandChild( 5, 'classic' )).one().value() === 6 return (new ClassicGrandChild( 5, 'classic' )).two().value() === 5 return (new ClassicGrandChild( 5, 'classic' )).three().value() === 6

return (new BlackBoxGrandChild( 5, 'blackBox' )).value() === 5 return (new BlackBoxGrandChild( 5, 'blackBox' )).increase().value() === 6 return (new BlackBoxGrandChild( 5, 'blackBox' )).decrease().value() === 4 return (new BlackBoxGrandChild( 5, 'blackBox' )).reset().value() === 0 return (new BlackBoxGrandChild( 5, 'blackBox' )).one().value() === 6 return (new BlackBoxGrandChild( 5, 'blackBox' )).two().value() === 5 return (new BlackBoxGrandChild( 5, 'blackBox' )).three().value() === 6

return FunctorGrandChild( 5, 'functor' ).value() === 5 return FunctorGrandChild( 5, 'functor' ).increase().value() === 6 return FunctorGrandChild( 5, 'functor' ).decrease().value() === 4 return FunctorGrandChild( 5, 'functor' ).reset().value() === 0 return FunctorGrandChild( 5, 'functor' ).one().value() === 6 return FunctorGrandChild( 5, 'functor' ).two().value() === 5 return FunctorGrandChild( 5, 'functor' ).three().value() === 6

Free Web Hosting