It's a similar pattern that arises in many programming languages - the need for a function to accept differing structures of arguments - and is the reason why certain languages have built-in support for this behaviour, with the use of functional overloading.
In JavaScript however, no such ability exists - and with each of my pure js projects over the last 4 or 5 years, I kept getting rather bored with writing and rewriting the same kind of checks at the top of a function to handle a range of different ways the entity could be called.
So I've been playing with a solution to this idea for a while, but each of my implementations always felt like over coding the issue. JavaScript should be light, quick and powerful - most of what I came up with wasn't.
However, having reached the same situation for the umpteenth time. I finally threw together a solution that seems to be exactly what I've needed, so far at least, and is a lot simpler than previous versions:
var describe = (function(){
var describe = function( obj, opt, mod, looped ){
var m,i,s,t = describe.type(obj), w = describe.type(opt);
if ( w === 'number' || w === 'array' ) {
mod = opt; w = opt = null;
}
if ( describe.type( mod ) == 'number' ) {
m = mod
}
else {
m = describe.TYPES
};
if ( (t === 'array') && (!looped || m & describe.ARRAYS) ) {
s = '';
for ( i in obj ) {
if ( obj.hasOwnProperty && !obj.hasOwnProperty(i) ) {continue;};
s += (s?',':'')
+ describe(obj[i], mod && mod[i] ? mod[i] : m, null, true);
}
t = '['+s+']';
m = null;
}
if ( w === 'object' ) {
if ( opt[t] ) {
opt[t].apply(describe,obj);
}
else if (opt['*']) {
opt['*'].apply(describe,obj);
}
}
else if ( m ) {
s = '';
if ( m & describe.VALUES ) {
s += String(obj);
}
else {
s += (s?':':'') + t;
}
t = s;
}
return t;
}
describe.TYPES = 1;
describe.VALUES = 2;
describe.ARRAYS = 4;
describe.type = function(obj,t){
if (
(t = typeof obj) &&
t === 'string' ||
t === 'number' ||
t === 'boolean' ||
t === 'function' ) {
/// ILB
}
else if ( t == 'undefined' || obj === null ) {
t = 'null';
}
else if(
(t == 'array') ||
(window.Array && obj instanceof Array) ||
(obj.join && obj.slice && obj.unshift) ||
(obj[0] && obj.length) ||
(window.jQuery && obj instanceof jQuery)
){
t = 'array';
}
else if (
(obj.call && obj.apply) ||
(window.Function && obj instanceof Function)
){
t = 'function';
}
else if ( obj.location && obj.open ){
t = 'window';
}
else if ( obj.getElementById ){
t = 'document';
}
else if ( obj.getElementsByTagName ){
t = 'element';
}
return t;
}
return describe;
})();
The idea is a simple one, which could (and will) be improved over time. It's based mainly on type checking, but it can also handle value checking. A basic implementation would be as follows:
var my_function_to_call = function(a,b,c,d){
describe(arguments,{
'[object,object]':function(a,b){
/// do what you need to set-up if you get two objects
},
'[string,object,number]':function(a,b,c){
/// do what you need to set-up given a string, object and a number
}
'*':function(a,b){
/// fallback for anything else
}
});
}
The above makes for much easier reading, and even though this example does create and destroy several temporary functions with each my_function_to_call() -- for most of my core functions that might only get called once or twice it works very well. Obviously you could optimise by keeping your second parameter for describe defined outside of my_function_to_call, but if you are really looking for optimal high-frequency code you should be using the standard js methods of type checking.
A few other ways the code can be used:
var test = function(name, step){
describe(arguments,{
'[helloworld:string,123:number]':function(a,b){
/// do something when specific values and types are passed
},
}, describe.VALUES | describe.TYPES);
describe(arguments,{
'[helloworld,123]':function(a,b){
/// do something when specific values are passed
},
}, describe.VALUES);
describe(arguments,{
'[helloworld,number]':function(a,b){
/// do something when a specific value and specific type is passed
},
}, [describe.VALUES, describe.TYPES]);
}
test('helloworld', 10);
The astute readers out there will notice that this function basically just describes whatever object is passed into it, in simple terms. The secondary parameter is a bonus, as it allows the generated descriptive string to be checked against an object of methods. So you can use the above function just to aid in type checking:
if ( describe(param) == 'array' ) {
/// do something with an array - the code uses a duck test
/// so if something quacks like an array - i.e. jQuery it'll
/// be called an array. I do have another version of this code
/// that is specifically designed to return jQuery for jQuery objs.
}
This is just a starting out point, but so far I like the way things are working, so I shall continue...
Below you will find a minimized version of the above code:
var describe=(function(){var a=function(b,c,d,e){var f,g,h,i=a.type(b),j=a.type(c);
if(j==="number"||j==="array"){d=c;j=c=null}if(a.type(d)=="number"){f=d}
else{f=a.TYPES}if(i==="array"&&(!e||f&a.ARRAYS)){h="";
for(g in b){if(b.hasOwnProperty&&!b.hasOwnProperty(g)){continue}
h+=(h?",":"")+a(b[g],d&&d[g]?d[g]:f,null,true)}i="["+h+"]";f=null}
if(j==="object"){if(c[i]){c[i].apply(a,b)}else if(c["*"]){c["*"].apply(a,b)}}
else if(f){h="";if(f&a.VALUES){h+=String(b)}else{h+=(h?":":"")+i}i=h}
return i};a.TYPES=1;a.VALUES=2;a.ARRAYS=4;a.type=function(a,b){
if((b=typeof a)&&b==="string"||b==="number"||b==="boolean"||b==="function"){}
else if(b=="undefined"||b===null){b="null"}
else if(b=="array"||window.Array&&a instanceof Array||
a.join&&a.slice&&a.unshift||a[0]&&a.length||
window.jQuery&&a instanceof jQuery){b="array"}
else if(a.call&&a.apply||window.Function&&a instanceof Function){b="function"}
else if(a.location&&a.open){b="window"}else if(a.getElementById){b="document"}
else if(a.getElementsByTagName){b="element"}return b};return a})();
- One comment • Category: Coding, JavaScript
- Share on Twitter, Facebook, Delicious, Digg, Reddit
One comment
China's immigration bureau in a press release on WeChat stated mainland residents can travel to the gaming hub through an online visa system beginning November 1. The transfer is expected to 먹튀사이트 먹튀프렌즈 extend the number of vacationers to Macao. Hong Kong-listed Macao gaming stocks pared losses after a visa policy replace for mainland Chinese residents seeking to travel to Macao. Ocado will construct a network of automated warehouses for the corporate, with six planned by 2028, where robots pick meals and other gadgets for delivery to shoppers.
by dabneygahr on 7 November 2022 at 05:43. #