If I have the function:
function(foo, bar, baz);
And I want to allow for both named arguments and normal function calls, what is the best way of handling this? In php you can extract the variables into the local namespace but as far as I know the only way to handle this in javascript is by handling both scenarios separately. I've given a code example below:
function(foo, bar, baz)
{
if(typeof(foo) == 'object') // Named args
{
alert(foo.foo);
alert(foo.bar);
alert(foo.baz);
}
else
{
alert(foo);
alert(bar);
alert(baz);
}
}
myFunc('a', 'b', 'c');
myFunc({ foo: 'a', 开发者_Go百科bar: 'b', baz: 'c' });
Any javascript gurus out there who can teach me the ways of javascriptFu?
Since you cannot access the local scope dynamically (without evil eval
), you should consider the following approach:
var myFunc = function (foo, bar, baz) {
if (typeof(foo) === 'object') {
bar = foo.bar;
baz = foo.baz;
foo = foo.foo; // note: foo gets assigned after all other variables
}
alert(foo);
alert(bar);
alert(baz);
};
You simply translate the named args to regular variables manually. After that, your code will run for both cases without changes.
Do it with elegance:
var myFunc = (function (foo, bar, baz) {
// does whatever it is supposed to do
}).
withNamedArguments({foo:"default for foo", bar:"bar", baz:23 });
myFunc({foo:1}); // calls function(1, "bar", 23)
myFunc({}); // calls function("default for foo", "bar", 23);
myFunc({corrupt:1}); // calls function({corrupt:1})
myFunc([2,4], 1); //calls function([2,4], 1)
Even this one works
Array.prototype.slice =
Array.prototype.slice.withNamedArguments({start:0, length:undefined});
[1,2,3].slice({length:2}) //returns [1,2]
[1,2,3].slice(1,2) //returns [2,3]
... or here, parseInt()
parseInt = parseInt.withNamedArguments({str:undefined, base:10});
parseInt({str:"010"}); //returns 10
Just enhance the Function object:
Function.prototype.withNamedArguments = function( argumentList ) {
var actualFunction = this;
var idx=[];
var ids=[];
var argCount=0;
// construct index and ids lookup table
for ( var identifier in argumentList ){
idx[identifier] = argCount;
ids[argCount] = identifier;
argCount++;
}
return function( onlyArg ) {
var actualParams=[];
var namedArguments=false;
// determine call mode
if ( arguments.length == 1 && onlyArg instanceof Object ) {
namedArguments = true;
// assume named arguments at the moment
onlyArg = arguments[0];
for ( name in onlyArg )
if (name in argumentList ) {
actualParams[idx[name]] = onlyArg[name];
} else {
namedArguments = false;
break;
}
}
if ( namedArguments ) {
// fill in default values
for ( var i = 0; i < argCount; i++ ) {
if ( actualParams[i] === undefined )
actualParams[i] = argumentList[ids[i]];
}
} else
actualParams = arguments;
return actualFunction.apply( this, actualParams );
};
};
This is always awkward and not very rigourous but it's far safer to check the arguments for the absence of data than for a particular positive expectation, especially typeof on object.
Some variation on the below, the strategy here being to translate a DTO style input into a named argument style input (the opposite is also reasonable but I find less obvious). The advantage of this strategy is once you've passed this translation block, the rest of the code doesn't care how you got there.
// translate to named args - messy up front, cleaner to work with
function(foo, bar, baz)
{
// Opt 1: default to named arg, else try foo DTO
bar = (typeof(bar) != 'undefined' ? bar : foo.bar);
// Opt 2: default to named arg, else check if property of foo, else hard default (to null)
baz = (typeof(baz) != 'undefined' ? baz : typeof(foo.baz) != 'undefined' ? foo.baz : null);
// the first argument is always a problem to identify in itself
foo = (foo != null ? typeof(foo.foo) != 'undefined' ? foo.foo : foo : null);
}
// translate to object - cleaner up front, messier to work with
function(foo, bar, baz)
{
var input = (typeof(foo.foo) != 'undefined' ? foo : { 'foo' : foo, 'bar' : bar, 'baz' : baz });
}
The first arg (foo here) is always a problem because you expect it to be in one of two complex states (where the other args are always a single complex state or undefined) and you cannot process it until you've dealt with all the other args because obviously once you've changed it it's unreliable to use it for initialising anything else.
精彩评论