Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Свойства посредством полиморфных функций (https://javascript.ru/forum/project/8289-svojjstva-posredstvom-polimorfnykh-funkcijj.html)

tenshi 18.03.2010 17:12

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


начнём из далека...

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, время: 11:54.