Свойства посредством полиморфных функций
пока ИЕ не поддерживает свойств, приходится эмулировать их через функции. сейчас приходится отдельно задавать поле для хранения значения, отдельно геттер и сеттер. многие объединяют геттер и сеттер в одну полиморфную функцию. тут я расскажу как объединить их вмесе с полем в одно целое.
начнём из далека... Fpoly создаёт полиморфную функцию из массива функций. то, какой функции будет делегировано выполнение зависит от числа переданных параметров и индекса функции в массиве. var FPoly= new function(){ Version: 1 Description: 'creates polymorphic function by set of functions' License: 'public domain' Implementation: var FPoly= function( map, def ){ var poly= function(){ var map= poly._map var func= map[ arguments.length ] || map.def if( !func ) throw new Error( 'wrong parameters count' ) return func.apply( this, arguments ) } if( def ) map.def= def poly._map= map return poly } Export: return FPoly Usage: var obj= new function(){ this.title= FPoly( [ function(){ return document.title } , function( title ){ document.title= title return this } ]) } alert( obj.title( 'new title' ).title() ) // alerts 'new title' obj.title( 1, 2 ) // throws an exception } FPipe создаёт функцию-конвеер, которая прогоняет переданное значение через каждую функцию и возвращает результат. var FPipe= new function(){ Version: 1 Description: 'proceed value through array of functions' License: 'public domain' Implementation: var FPipe= function( funcs ){ if( !funcs || !funcs.length ) return function( val ){ return val } if( funcs.length === 1 ) return funcs[ 0 ] var pipe= function( val ){ var funcs= pipe._funcs var count= funcs.length for( var i= 0; i < count; ++i ) val= funcs[ i ].apply( this, [val] ) return val } pipe._funcs= funcs return pipe } Export: return FPipe Usage: var pipe= FPipe() alert( pipe( pipe( 'value' ) ) ) // alerts 'value' var convert= FPipe([ parseInt, Math.abs ]) alert( convert( '-2px' ) ) // alerts 2 } FField создаёт полиморфную функцию, которая сохраняет сама в сабе значение параметра. если параметр передан, то запоминает его и возвращает this. если не передан - возвращает его. var FField= new function(){ Version: 1 Description: 'property that storage any value in himself' License: 'public domain' Implementation: var FField= function( val ){ var field= function(){ return field._proc.apply( this, arguments ) } field._proc= FPoly( [ function(){ return field._val } , function( val ){ field._val= val return this } ]) field._val= val return field } Export: return FField Usage: this.content= FField( 0 ) alert( this.content() ) // alerts 0 alert( this.content( 'text' ).content() ) // alerts 'text' this.content( 1, 2 ) // throws an exception } FProp создаёт полиморфную функцию с тем же интерфейсом, что и FField, но позволяет задать дополнительно префильтр при сохранении значения и/или постфильтр при получении его. var FProp= new function(){ Version: 1 Description: 'composes getter, setter and storage of value' License: 'public domain' Implementation: var FProp= function( arg ){ var prop= function(){ return prop._proc.apply( this, arguments ) } var getters= [function(){ var val= prop._field.call( this ) return val }] if( arg.get ) getters.push( arg.get ) var setters= [function( val ){ prop._field.call( this, val ) return this }] if( arg.set ) setters.unshift( arg.set ) prop._proc= FPoly([ FPipe( getters ), FPipe( setters ) ]) prop._field= FField() if( 'val' in arg ) prop._proc.call( this, arg.val ) return prop } Export: return FProp Usage: var obj= new function(){ this.count= FProp({ set: parseInt, val: '1px', get: Object }) } alert( obj.count() ) // alerts 1 alert( obj.count( '2px' ).count() ) // alerts 2 alert( obj.count() === obj.count() ) // alerts false } FLazy создаёт метод, который создаёт другой метод вместо себя и делегирует выполнение ему. используется, например, для возможности наследования свойств от прототипа. var FLazy= new function(){ Version: 1 Description: 'creates a method on first access' License: 'public domain' Implementation: FLazy= function( factory ){ var lazy= function(){ for( var key in this ){ if( this[ key ] !== lazy ) continue var func= lazy._factory.apply( this ) this[ key ]= func return func.apply( this, arguments ) } throw new Error( 'can not find me in the object' ) } lazy._factory= factory return lazy } Export: return FLazy Usage: var Widget= function( id ){ this.id= id } Widget.prototype= new function(){ this.nodeContent= FLazy(function(){ var node= document.getElementById( this.id ) return function(){ return node } }) } var wd= new Widget( 'id-datepicker' ) var wc= new Widget( 'id-colorpicker' ) alert([ wd.nodeContent(), wc.nodeContent(), wd.nodeContent() === wc.nodeContent() ]) // alerts 2 elements and false } а теперь собственно то, ради чего это затевалось: var Widget= function(){} Widget.prototype= new function(){ this.vel= FLazy(function(){ return FProp({ set: parseInt, val: 0 }) }) } var one= ( new Widget ).vel( '5 cm per second' ) var two= ( new Widget ).vel( 13 ) alert([ one.vel(), two.vel() ]) // alerts 5, 13 |
Часовой пояс GMT +3, время: 09:17. |