Производительность яваскрипта
Создание объектов
Первый способ - классический. Мы определяем конструктор, в котором задаём внутреннее состояние объекта, а в прототип подмешиваем методы.
(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