2010-11-12 58 views
3

如果我具備的功能: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

既然你不能訪問動態局部範圍內(不惡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); 
}; 

您只需翻譯命名的參數傳遞給手動普通變量。之後,您的代碼將在兩種情況下運行而不發生任何更改。

+0

這實際上是我在思考我的問題一段時間後得出的結論。 – smack0007 2010-11-12 09:12:21

+0

@ smack0007:這是迄今爲止最簡單的解決方案。你可以(當然)以相反的方式做到這一點:確保所有東西都可以通過對象訪問。不過,這可能會更冗長些。 – jwueller 2010-11-12 09:59:03

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

的第一個參數(FOO這裏)始終是一個問題,因爲你希望它是在兩個複雜的國家之一(其中其他ARG遊戲總是一個複雜的狀態或不確定的),你不能處理它,直到你已經處理了所有其他的參數,因爲顯然一旦你改變了它,用它來初始化其他任何東西都是不可靠的。

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