Javascript处理具有命名Args和Normal的调用函数

时间:2010-11-12 08:45:47

标签: javascript

如果我有这个功能:

function(foo, bar, baz);

我想允许命名参数和普通函数调用,处理这个问题的最佳方法是什么?在php中你可以将变量提取到本地命名空间,但据我所知,在javascript中处理这个问题的唯一方法是分别处理这两个场景。我在下面给出了一个代码示例:

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', bar: 'b', baz: 'c' });

那里有哪些javascript专家可以教我javascriptFu的方式?

3 个答案:

答案 0 :(得分:3)

由于您无法动态访问本地范围(没有恶意eval),您应该考虑以下方法:

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);
};

您只需手动将命名的args转换为常规变量。之后,您的代码将针对这两种情况运行而无需更改。

答案 1 :(得分:1)

优雅地做到这一点:

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)

即便是这样的

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]

...或者这里,parseInt()

parseInt = parseInt.withNamedArguments({str:undefined, base:10});
parseInt({str:"010"}); //returns 10

只需增强Function对象:

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 );
    };
};

答案 2 :(得分:0)

这总是很尴尬而且不是非常严谨,但检查数据缺失的论据比特定的正面预期更安全,特别是对象的类型。

下面的一些变化,这里的策略是将DTO样式输入转换为命名参数样式输入(相反也是合理的,但我发现不太明显)。这个策略的优点是,一旦你通过了这个翻译块,其余的代码并不关心你是如何到达那里的。

// 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 });
}

第一个arg(这里的foo)总是一个问题,因为你希望它处于两个复杂状态之一(其他args总是一个复杂的状态或未定义),你不能处理它直到你&#39;已经处理了所有其他的args,因为很明显,一旦你改变了它,用它来初始化其他东西是不可靠的。