2010-11-12 1 views
3

Si j'ai la fonction:Manipulation Javascript avec des fonctions d'appel nommés Args et normal

function(foo, bar, baz); 

Je veux permettre à la fois les arguments nommés et les appels de fonction normale, quelle est la meilleure façon de gérer cela? En PHP, vous pouvez extraire les variables dans l'espace de noms local, mais pour autant que je sache, la seule façon de gérer cela en javascript est de gérer les deux scénarios séparément. J'ai donné un exemple de code ci-dessous:

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

Tous les gourous javascript là-bas qui peut me apprendre les voies de javascriptFu?

Répondre

3

Puisque vous ne pouvez pas accéder à la portée locale dynamique (sans mal eval), vous devriez envisager l'approche suivante:

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

vous traduisez simplement les args nommées à des variables régulières manuellement. Après cela, votre code sera exécuté pour les deux cas sans modifications.

+0

C'est en fait la conclusion à laquelle je suis parvenu après avoir réfléchi à ma question pendant un petit moment. – smack0007

+0

@ smack0007: C'est de loin la solution la plus simple. Vous pouvez (bien sûr) le faire aussi dans l'autre sens: Assurez-vous que tout est accessible via des objets. Ce serait un peu plus verbeux, cependant. – jwueller

0

Ceci est toujours gênant et pas très rigoureux, mais il est beaucoup plus sûr de vérifier les arguments pour l'absence de données que pour une attente positive particulière, en particulier typeof sur l'objet. Voici une variante de ce qui suit, la stratégie étant ici de traduire une entrée de style DTO dans une entrée de style d'argument nommé (l'inverse est également raisonnable mais je trouve moins évident). L'avantage de cette stratégie est qu'une fois que vous avez passé ce bloc de traduction, le reste du code ne se soucie pas de savoir comment vous y êtes arrivé.

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

Le premier arg (foo ici) est toujours un problème parce que vous attendez qu'il soit dans l'un des deux états complexes (où les autres args sont toujours un état complexe unique ou indéfini) et vous ne pouvez pas traiter jusqu'à vous avez traité tous les autres args parce que, évidemment, une fois que vous avez changé, il n'est pas fiable de l'utiliser pour initialiser autre chose.

1

le faire avec élégance:

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) 

Même celui-ci fonctionne

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] 

... ou ici, parseInt()

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

l'objet ne font qu'accentuer la fonction:

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