Nombre variable d'arguments JavaScript pour fonctionner

531

Existe-t-il un moyen d'autoriser les variables "illimitées" pour une fonction en JavaScript?

Exemple:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Timo
la source
doublon lié / possible de stackoverflow.com/questions/4633125/…
Luke
1
@Luke non, ce n'est pas le cas. Cette question demande comment appeler une fonction avec un nombre arbitraire d'arguments avec les arguments d'un tableau. Cela demande comment gérer un tel appel.
Scruffy
1
Pour faciliter la recherche, une telle fonction est appelée «fonction variadique».
gschenk

Réponses:

814

Bien sûr, utilisez simplement l' argumentsobjet.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
roufamatic
la source
1
Tnx. Il est idéal pour analyser les chaînes d'un code natif Android vers javascript dans une vue Web.
Johan Hoeksma
4
Cette solution a fonctionné le mieux pour moi. Merci. Plus d'informations sur le mot-clé arguments ICI .
User2
26
argumentsest un objet spécial "de type tableau", ce qui signifie qu'il a une longueur, mais pas d'autres fonctions de tableau. Voir developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… pour plus d'informations et cette réponse: stackoverflow.com/a/13145228/1766230
Luke
4
Fait intéressant, les méthodes Array en Javascript ont été définies de telle manière qu'elles fonctionnent sur tout objet possédant une lengthpropriété. Cela inclut l' argumentsobjet. Sachant cela, et que la méthode concatretourne une copie du « tableau » , il est appelé, nous pouvons facilement convertir l' argumentsobjet vers un tableau comme celui - ci: var args = [].concat.call(arguments). Certaines personnes préfèrent utiliser à la Array.prototype.concat.callplace, mais j'aime le [], elles sont courtes et douces!
Stijn de Witt
2
@YasirJan use[...arguments].join()
Willian D. Andrade
179

Dans les navigateurs les plus récents, vous pouvez accepter un nombre variable d'arguments avec cette syntaxe:

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

Voici un petit exemple:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
    0: "a"
    1: "b"
    2: "c"
    3: "d"
    length: 4

Documentation et autres exemples ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Ramast
la source
27
Pour info, il est appelé "la syntaxe des paramètres de repos": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k
4
+1 C'est une solution élégante et propre. Particulièrement adapté pour passer à travers une longue liste de paramètres dans un autre appel de fonction, et avec possible que ces paramètres variables soient à une position arbitraire.
haxpor
3
Gardez à l'esprit que, selon developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…, il n'est pas pris en charge sur IE.
Roee Gavirel
2
Voici un tableau montrant la prise en charge du navigateur - caniuse.com/#feat=rest-parameters
Brian Burns
1
Une si belle réponse!
Ashish
113

Une autre option consiste à passer vos arguments dans un objet contextuel.

function load(context)
{
    // do whatever with context.name, context.address, etc
}

et l'utiliser comme ça

load({name:'Ken',address:'secret',unused:true})

Cela a l'avantage que vous pouvez ajouter autant d'arguments nommés que vous le souhaitez, et la fonction peut les utiliser (ou non) comme bon lui semble.

Ken
la source
4
Ce serait mieux car il supprime le couplage à l'ordre des arguments. Les interfaces à couplage lâche sont une bonne pratique standard ...
Jonas Schubert Erlandsson
9
Bien sûr, c'est mieux dans certains cas. Mais disons que les arguments individuels ne sont pas vraiment liés les uns aux autres, ou sont tous censés avoir la même signification (comme les éléments du tableau). Alors la voie de l'OP est la meilleure.
rvighne
1
C'est aussi bien parce que si vous le souhaitez, vous pouvez créer l' contextargument avec du code et le transmettre avant qu'il ne soit utilisé.
Nate CK
46

Je suis d'accord avec la réponse de Ken comme étant la plus dynamique et j'aime aller plus loin. Si c'est une fonction que vous appelez plusieurs fois avec des arguments différents - j'utilise la conception de Ken mais j'ajoute ensuite des valeurs par défaut:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

De cette façon, si vous avez de nombreux paramètres mais n'avez pas nécessairement besoin de les définir à chaque appel à la fonction, vous pouvez simplement spécifier les valeurs par défaut. Pour la méthode extend, vous pouvez utiliser la méthode extend ( $.extend()) de jQuery , créer la vôtre ou utiliser ce qui suit:

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

Cela fusionnera l'objet contextuel avec les valeurs par défaut et remplira toutes les valeurs non définies dans votre objet avec les valeurs par défaut.

mbeasley
la source
3
+1. Joli tour. Économise beaucoup de plaque de chaudière pour que chaque paramètre soit défini, par défaut ou autrement.
Neil
1
La _.defaults() méthode Underscore est une très bonne alternative pour fusionner les arguments spécifiés et par défaut.
mbeasley
18

Oui, juste comme ça:

function load()
{
  var var0 = arguments[0];
  var var1 = arguments[1];
}

load(1,2);
Canavar
la source
18

Il est préférable d'utiliser la syntaxe des paramètres de repos comme l'a souligné Ramast.

function (a, b, ...args) {}

Je veux juste ajouter une belle propriété de l'argument ... args

  1. C'est un tableau et non un objet comme des arguments. Cela vous permet d'appliquer des fonctions comme la carte ou de trier directement.
  2. Il n'inclut pas tous les paramètres mais seulement celui qui en est passé. Par exemple, la fonction (a, b, ... args) dans ce cas, args contient l'argument 3 à arguments.length
Nicola Pedretti
la source
15

Comme déjà mentionné, vous pouvez utiliser le arguments objet pour récupérer un nombre variable de paramètres de fonction.

Si vous souhaitez appeler une autre fonction avec les mêmes arguments, utilisez apply. Vous pouvez même ajouter ou supprimer des arguments en les convertissant argumentsen tableau. Par exemple, cette fonction insère du texte avant de se connecter à la console:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Peter Tseng
la source
belle solution pour convertir des arguments en tableau. Cela m'a été utile aujourd'hui.
Dmytro Medvid
8

Bien que je convienne généralement que l'approche des arguments nommés est utile et flexible (sauf si vous vous souciez de l'ordre, auquel cas les arguments sont les plus faciles), j'ai des inquiétudes concernant le coût de l'approche mbeasley (en utilisant les valeurs par défaut et les extensions). Il s'agit d'un coût extrême à prendre pour extraire les valeurs par défaut. Tout d'abord, les valeurs par défaut sont définies à l'intérieur de la fonction, elles sont donc repeuplées à chaque appel. Deuxièmement, vous pouvez facilement lire les valeurs nommées et définir les valeurs par défaut en même temps en utilisant ||. Il n'est pas nécessaire de créer et de fusionner un autre nouvel objet pour obtenir ces informations.

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

C'est à peu près la même quantité de code (peut-être un peu plus), mais devrait représenter une fraction du coût d'exécution.

mcurland
la source
D'accord, bien que le préjudice dépende du type de valeur ou du défaut lui-même. Sinon, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)ou pour moins de volume de code, une petite fonction d'assistance comme: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)fournir des modèles alternatifs. Cependant, mon argument est toujours valable: créer des objets supplémentaires pour chaque invocation de fonction et exécuter des boucles coûteuses pour définir quelques valeurs par défaut est une quantité folle de surcharge.
mcurland
7

Alors que @roufamatic a montré l'utilisation du mot-clé arguments et @Ken a montré un excellent exemple d'un objet à utiliser, je pense que ni vraiment abordé ce qui se passe dans ce cas et peut confondre les futurs lecteurs ou inculquer une mauvaise pratique comme n'indiquant pas explicitement une fonction La méthode / est destinée à prendre une quantité variable d'arguments / paramètres.

function varyArg () {
    return arguments[0] + arguments[1];
}

Lorsqu'un autre développeur examine votre code, il est très facile de supposer que cette fonction ne prend pas de paramètres. Surtout si ce développeur n'est pas au courant des arguments mot-clé . Pour cette raison, c'est une bonne idée de suivre une directive de style et d'être cohérent. J'utiliserai Google pour tous les exemples.

Disons explicitement que la même fonction a des paramètres variables:

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

Paramètre d'objet VS var_args

Il peut y avoir des moments où un objet est nécessaire car c'est la seule méthode recommandée et considérée comme la meilleure pratique d'une carte de données. Les tableaux associatifs sont désapprouvés et découragés.

SIDENOTE: Le mot-clé arguments renvoie en fait un objet en utilisant des nombres comme clé. L'héritage prototypique est également la famille d'objets. Voir la fin de la réponse pour une utilisation correcte de la baie dans JS

Dans ce cas, nous pouvons également le dire explicitement. Remarque: cette convention de dénomination n'est pas fournie par Google mais est un exemple de déclaration explicite du type d'un paramètre. Ceci est important si vous cherchez à créer un modèle typé plus strict dans votre code.

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

Lequel choisir?

Cela dépend des besoins de votre fonction et de votre programme. Si, par exemple, vous cherchez simplement à renvoyer une base de valeurs sur un processus itératif à travers tous les arguments passés, restez très certainement avec le mot-clé arguments . Si vous avez besoin de définir vos arguments et de mapper les données, la méthode objet est la solution. Regardons deux exemples et nous avons terminé!

Utilisation des arguments

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

Utilisation des objets

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

Accéder à un tableau au lieu d'un objet ("... args" Le paramètre reste)

Comme mentionné en haut de la réponse, le mot-clé arguments renvoie en fait un objet. Pour cette raison, toute méthode que vous souhaitez utiliser pour un tableau devra être appelée. Un exemple de ceci:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

Pour éviter cela, utilisez le paramètre rest:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Brian Ellis
la source
5

Utilisez l' argumentsobjet à l'intérieur de la fonction pour avoir accès à tous les arguments transmis.

nickytonline
la source
3

Sachez que le passage d'un objet avec des propriétés nommées comme l'a suggéré Ken ajoute le coût d'allocation et de libération de l'objet temporaire à chaque appel. Passer des arguments normaux par valeur ou référence sera généralement le plus efficace. Pour de nombreuses applications, les performances ne sont pas critiques, mais pour certaines, elles peuvent l'être.

phbcanada
la source