пока ИЕ не поддерживает свойств, приходится эмулировать их через функции. сейчас приходится отдельно задавать поле для хранения значения, отдельно геттер и сеттер. многие объединяют геттер и сеттер в одну полиморфную функцию. тут я расскажу как объединить их вмесе с полем в одно целое.
начнём из далека...
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