PDA

View Full Version : Ext.overload (javascript Function overloading)



hendricd
23 Sep 2008, 8:27 AM
Here is an addition for the Ext framework that may be of interest.

It permits javascript function overloading (yes, constructors too) as found in other programming languages (PHP, C++, Python, etc). For performance reasons, this implementation permits selective function invokation based on the number(only) of parameters passed as arguments.


Syntax:

Creates an new overloaded function:

var ovlFN = Ext.overload(fn);
or
var ovlFN = Ext.overload([fn1, fn2]); //define multiple call signatures Overloads an existing function:


var ovlFN = Ext.overload(targetFn, [fn,....] );Overload/create a new overloaded class method:

Ext.overload(Object, memberName , [fn,....] );

eg: Ext.overload(Ext.DomHelper, 'markup' , [fn,....] );
Samples:

//Use it with Ext classes !
var Person = Ext.extend(Object, {

constructor : Ext.overload([
function(setup){ //single argument form

Ext.apply(this,
setup||{},
{name:'unknown', age: 0});

},
function(setup, kids){ //double-argument form

this.constructor(setup); //calling the single argument form
//kids are people too
this.kids = (kids||[]).map(
function(kid){return new Person({name:kid,age:3,parent:this})}
);
}]),

sayName : Ext.overload([
function(){ return this.name },
function(withFn) { withFn(this.name); }
])

});


//Overload the class's sayName method again:
Ext.overload ( Person.prototype, 'sayName',
function(kid, withFn) { withFn(this.kids[kid].name); }
);

//Add an overloaded getter
Person.prototype.get = Ext.overload ([
function(member, encoder) {
return typeof encoder == 'function' ?
encoder.call(null,this.get(member)) :
this.get(member);

},
function(member) { return this[member]; }
]);


var p = new Person({name:'Dan'});
p.sayName(console.log);

//use the overloaded constructor for the family
var family = new Person({name:'Dan',age:30},['Tommy', 'Sally']);
console.log( family.kids[0].sayName()) ;
family.sayName(1, console.log); //log Sally to the console

var Dans_Age = family.get('age');
var Dan_Upper = family.get('name',String.toUpperCase);

//create an overloaded debug-writer
var debug = Ext.overload([
//single-argument form
function(text){ console.log(text); },

//double-argument form
function(text, obj){
console.log(text);
console.dir(obj);
}
]);

debug('All about Dan:', family );

The Source:


/* Copyright 2008, Doug Hendricks, Active Group, Inc. All rights reserved.
* License: LGPL 3.0
* @version 1.0
*/
(function(){

var overload = function(pfn, fn ){

var f = typeof pfn =='function' ? pfn : function(){};

var ov = f._ovl; //call signature hash
if(!ov){
ov = {base:f};
ov[f.length|| 0] = f;
f= function(){
var o = arguments.callee._ovl;
var fn = o[arguments.length]||o.base;
//recursion safety
return fn && fn != arguments.callee ? fn.apply(this,arguments): undefined;
};
}
var fnA = [].concat(fn);
for(var i=0,l=fnA.length; i<l; i++){
//ensure no duplicate call signatures, but last in rules!
ov[fnA[i].length] = fnA[i];
}
f._ovl= ov;
return f;

};

//Give Ext.overload various call signatures.
Ext.overload = overload( overload,
[
function(fn){ return overload(null, fn);},
function(obj, mname, fn){
return obj[mname] = overload(obj[mname],fn);}
]);


})();

harley.333
23 Sep 2008, 9:06 AM
Cool. I think this is a pretty good addition to the JavaScript arsenal. As long as no one gets carried away and tries to make a "type-safe" version, I think it's great!

SamuraiJack1
24 Sep 2008, 12:52 AM
Really good idea.

Think it should be matured to the state, when it will be possible to differentiate scalars, objects and arrays.

stamat
10 Apr 2013, 2:44 AM
I tried do find an elegant way to do function overloading, and here is what I came up with:


var out = def({
'int': function(a) {
alert('Here is int '+a);
},


'float': function(a) {
alert('Here is float '+a);
},


'string': function(a) {
alert('Here is string '+a);
},


'int,string': function(a, b) {
alert('Here is an int '+a+' and a string '+b);
},
'default': function(obj) {
alert('Here is some other value '+ obj);
}


});


out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

The code that enables this to work:

var def = function(functions, parent) {
return function() {
var types = [];
var args = [];
eachArg(arguments, function(i, elem) {
args.push(elem);
types.push(whatis(elem));
});
if(functions.hasOwnProperty(types.join())) {
return functions[types.join()].apply(parent, args);
} else {
if (typeof functions === 'function')
return functions.apply(parent, args);
if (functions.hasOwnProperty('default'))
return functions['default'].apply(parent, args);
}
};
};


var eachArg = function(args, fn) {
var i = 0;
while (args.hasOwnProperty(i)) {
if(fn !== undefined)
fn(i, args[i]);
i++;
}
return i-1;
};


var whatis = function(val) {


if(val === undefined)
return 'undefined';
if(val === null)
return 'null';


var type = typeof val;


if(type === 'object') {
if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
return 'array';
if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
return 'date';
if(val.hasOwnProperty('toExponential'))
type = 'number';
if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
retur n 'float';
else
return 'int';
}


return type;
};

Blog post: http://stamat.wordpress.com/2013/04/10/javascript-function-overloading/
JSFiddle: http://jsfiddle.net/stamat/Geh5r/

Malte123
3 Jan 2014, 6:38 AM
If you plan to use JSDuck for documenting your Code ...


sayName : Ext.overload([
function(){ return this.name },
function(withFn) { withFn(this.name); }
])


it only brings "Duplicate method name" and "Unnamed method" Warnings.

Generated Class-Description in JSDuck is not usable.

StudentDaniel
3 Jan 2014, 6:51 AM
BUG is now reported - hope next version can manage this :-)

https://github.com/senchalabs/jsduck/issues/519 (https://github.com/senchalabs/jsduck/issues/519)