Est-il possible d'envoyer un nombre variable d'arguments à une fonction JavaScript?

171

Est-il possible d'envoyer un nombre variable d'arguments à une fonction JavaScript, à partir d'un tableau?

var arr = ['a','b','c']

var func = function()
{
    // debug 
    alert(arguments.length);
    //
    for(arg in arguments)
        alert(arg);
}

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'

J'ai récemment écrit beaucoup de Python et c'est un modèle merveilleux pour pouvoir accepter des varargs et les envoyer. par exemple

def func(*args):
   print len(args)
   for i in args:
       print i

func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(*arr) // prints 4 which is what I want, then 'a','b','c','d'

Est-il possible en JavaScript d'envoyer un tableau à traiter comme le tableau d'arguments?

Corbeau de feu
la source
4
Notez que ce n'est pas une bonne idée d'utiliser une for - inboucle avec l' argumentsobjet - une boucle `` normale '' pour itérer sur la lengthpropriété doit être utilisée à la place
Yi Jiang
2
cela n'a jamais été un problème, pouvez-vous expliquer pourquoi c'est le cas? arguments object est presque toujours assez petit pour avoir une amélioration négligeable des performances pour l'utilisation de la version agruments [0..length-1].
Fire Crow
Possible duplication de la variable JavaScript nombre d'arguments pour fonctionner
Anderson Green
1
Je pense que les arguments ne sont pas un tableau réel, mais un objet spécial, donc la syntaxe "in" peut ne pas fonctionner, selon l'implémentation JS.
O'Rooney

Réponses:

209

Mise à jour : depuis ES6, vous pouvez simplement utiliser la syntaxe de diffusion lors de l'appel de la fonction:

func(...arr);

Depuis ES6 également si vous prévoyez de traiter vos arguments comme un tableau, vous pouvez également utiliser la syntaxe de propagation dans la liste de paramètres, par exemple:

function func(...args) {
  args.forEach(arg => console.log(arg))
}

const values = ['a', 'b', 'c']
func(...values)
func(1, 2, 3)

Et vous pouvez le combiner avec des paramètres normaux, par exemple si vous souhaitez recevoir les deux premiers arguments séparément et le reste sous forme de tableau:

function func(first, second, ...theRest) {
  //...
}

Et peut-être vous est-il utile de savoir combien d'arguments une fonction attend:

var test = function (one, two, three) {}; 
test.length == 3;

Mais de toute façon, vous pouvez passer un nombre arbitraire d'arguments ...

La syntaxe de diffusion est plus courte et "plus douce" que applyet si vous n'avez pas besoin de définir la thisvaleur dans l'appel de fonction, c'est la voie à suivre.

Voici un exemple d' application , qui était l'ancienne façon de le faire:

var arr = ['a','b','c'];

function func() {
  console.log(this); // 'test'
  console.log(arguments.length); // 3

  for(var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }

};

func.apply('test', arr);

De nos jours, je recommande de n'utiliser applyque si vous devez passer un nombre arbitraire d'arguments à partir d'un tableau et définir la thisvaleur. applyprend est la thisvaleur en tant que premiers arguments, qui sera utilisée lors de l'invocation de la fonction, si nous utilisons nulldans un code non strict, le thismot - clé fera référence à l'objet Global (fenêtre) à l'intérieur func, en mode strict, lors de l'utilisation explicite de 'use strict 'ou dans les modules ES, nullseront utilisés.

Notez également que l' argumentsobjet n'est pas vraiment un tableau, vous pouvez le convertir en:

var argsArray = Array.prototype.slice.call(arguments);

Et dans ES6:

const argsArray = [...arguments] // or Array.from(arguments)

Mais vous utilisez rarement l' argumentsobjet directement de nos jours grâce à la syntaxe de diffusion.

CMS
la source
1
Merci, appliquer fait l'affaire, appeler dans ce cas ne fonctionne pas. cela a-t-il été écrit par erreur?
Fire Crow
Comment faites-vous si la fonction est en fait un membre d'une classe? Est-ce correct? theObject.member.apply (theObject, arguments);
Baxissimo
4
Array.prototype.slice.call(arguments);empêche les optimisations JS du navigateur comme V8. Détails ici
Dheeraj Bhaskar
2
1. Apparemment, il argumentsest prévu d'être déprécié en faveur de l'opérateur de diffusion . Je n'ai pas de source pour cela, mais je l'ai entendu quelque part. 2. MDN déclare que l'utilisation slicesur l' argumentsobjet empêche les optimisations dans les moteurs comme V8. Ils suggèrent d'utiliser le "constructeur Array méprisé" Array.from(arguments), ou [...arguments]. Souhaitez-vous mettre à jour votre réponse pour ES2015?
Braden Best
NB: je suis allé de l'avant et j'ai écrit ma propre réponse .
Braden Best
75

Vous pouvez en fait transmettre autant de valeurs que vous le souhaitez à n'importe quelle fonction javascript. Les paramètres nommés explicitement obtiendront les premières valeurs, mais TOUS les paramètres seront stockés dans le tableau d'arguments.

Pour passer le tableau d'arguments sous forme "décompressé", vous pouvez utiliser apply, comme ceci (cf Javascript fonctionnel ):

var otherFunc = function() {
   alert(arguments.length); // Outputs: 10
}

var myFunc = function() {
  alert(arguments.length); // Outputs: 10
  otherFunc.apply(this, arguments);
}
myFunc(1,2,3,4,5,6,7,8,9,10);
Danben
la source
23

Les opérateurs splat et spread font partie de ES6, la prochaine version prévue de Javascript. Jusqu'à présent, seul Firefox les prend en charge. Ce code fonctionne dans FF16 +:

var arr = ['quick', 'brown', 'lazy'];

var sprintf = function(str, ...args)
{
    for (arg of args) {
        str = str.replace(/%s/, arg);
    }
    return str;
}

sprintf.apply(null, ['The %s %s fox jumps over the %s dog.', ...arr]);
sprintf('The %s %s fox jumps over the %s dog.', 'slow', 'red', 'sleeping');

Notez la syntaxe awkard pour la diffusion. La syntaxe habituelle de sprintf('The %s %s fox jumps over the %s dog.', ...arr);n'est pas encore prise en charge . Vous pouvez trouver un tableau de compatibilité ES6 ici .

Notez également l'utilisation de for...of, un autre ajout d'ES6. Utiliser for...inpour les tableaux est une mauvaise idée .

Tgr
la source
La syntaxe normale pour la diffusion est désormais prise en charge dans Firefox et Chrome
utilisateur
8

La applyfonction prend deux arguments; l'objet thissera lié et les arguments représentés par un tableau.

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"
August Lilleaas
la source
8

Il existe deux méthodes à partir de ES2015.

L' argumentsobjet

Cet objet est intégré aux fonctions et fait référence, de manière appropriée, aux arguments d'une fonction. Cependant, ce n'est pas techniquement un tableau, donc les opérations typiques du tableau ne fonctionneront pas dessus. La méthode suggérée consiste à utiliser Array.fromou l'opérateur spread pour créer un tableau à partir de celui-ci.

J'ai vu d'autres réponses mentionner l'utilisation slice. Ne fais pas ça. Il empêche les optimisations (source: MDN ).

Array.from(arguments)
[...arguments]

Cependant, je dirais que argumentsc'est problématique car cela masque ce qu'une fonction accepte comme entrée. Une argumentsfonction s'écrit généralement comme ceci:

function mean(){
    let args = [...arguments];
    return args.reduce((a,b)=>a+b) / args.length;
}

Parfois, l'en-tête de la fonction est écrit comme suit pour documenter les arguments à la manière du C:

function mean(/* ... */){ ... }

Mais c'est rare.

Quant à savoir pourquoi c'est problématique, prenez C, par exemple. C est rétrocompatible avec un ancien dialecte pré-ANSI du langage connu sous le nom de K&R C. K&R C permet aux prototypes de fonctions d'avoir une liste d'arguments vide.

int myFunction();
/* This function accepts unspecified parameters */

ANSI C fournit une fonctionnalité pour varargs( ...), et voidpour spécifier une liste d'arguments vide.

int myFunction(void);
/* This function accepts no parameters */

Beaucoup de gens déclarent par inadvertance des fonctions avec une unspecifiedliste d'arguments ( int myfunction();), alors qu'ils s'attendent à ce que la fonction n'accepte aucun argument. Ce qui est techniquement un bug car la fonction va accepter des arguments. N'importe quel nombre d'entre eux.

Une varargsfonction propre en C prend la forme:

int myFunction(int nargs, ...);

Et JavaScript a en fait quelque chose de similaire à cela.

Spread Operator / Paramètres de repos

Je vous ai déjà montré l'opérateur de diffusion, en fait.

...name

Il est assez polyvalent et peut également être utilisé dans la liste d'arguments d'une fonction ("paramètres de repos") pour spécifier des varargs d'une manière bien documentée:

function mean(...args){
    return args.reduce((a,b)=>a+b) / args.length;
}

Ou comme une expression lambda:

((...args)=>args.reduce((a,b)=>a+b) / args.length)(1,2,3,4,5); // 3

Je préfère de loin l'opérateur de propagation. C'est propre et auto-documenté.

Braden Best
la source
2
merci pour la réponse mise à jour, cet article est antérieur à l'ES 2015, donc je suis heureux que vous ayez rempli les options les plus récentes
Fire Crow
5

Cela s'appelle l' splatopérateur. Vous pouvez le faire en JavaScript en utilisant apply:

var arr = ['a','b','c','d'];
var func = function() {
    // debug 
    console.log(arguments.length);
    console.log(arguments);
}
func('a','b','c','d'); // prints 4 which is what I want, then 'a','b','c','d'
func(arr); // prints 1, then 'Array'
func.apply(null, arr); 
Chandra Patni
la source
4

Avec ES6, vous pouvez utiliser des paramètres de repos pour varagrs. Cela prend la liste d'arguments et la convertit en tableau.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}
theres.yer.problem
la source
2

Ceci est un exemple de programme pour calculer la somme des entiers pour les arguments de variable et le tableau sur les entiers. J'espère que cela t'aides.

var CalculateSum = function(){
    calculateSumService.apply( null, arguments );
}

var calculateSumService = function(){
    var sum = 0;

    if( arguments.length === 1){
        var args = arguments[0];

        for(var i = 0;i<args.length; i++){
           sum += args[i]; 
        }
    }else{
        for(var i = 0;i<arguments.length; i++){
           sum += arguments[i]; 
        }        
    }

     alert(sum);

}


//Sample method call

// CalculateSum(10,20,30);         

// CalculateSum([10,20,30,40,50]);

// CalculateSum(10,20);
traînée
la source
1
Quelques notes: 1. vardéclare une function scopevariable. Les nouveaux mots let- constclés et (ES 2015) déclarent des block scopevariables. Avant l'ES 2015, cependant, les variables auraient dû toujours être hissées au sommet de la fonction par pratique. 2. vos pauses de fonction lorsque donné 1 valeur entière à cause de votre acceptation erronée d'un tableau comme un argument (la question demande à varargs à propos, ne pas accepter des tableaux comme arguments): CalculateSum(6) -> 0. (suite dans l'article suivant)
Braden Best
1
3. N'utilisez pas de fonctions de débogage comme alert. En fait, ne l'utilisez pas alert/prompt/confirm, point final. Utilisez console.logplutôt lors du débogage. 4. La fonction doit être returnune valeur et non une valeur alert. 5. éviter à PascalCasemoins qu'il ne se réfère à un «type» de données. Ie une classe. Utilisez camelCaseou à la under_scoreplace. 4. Je n'aime pas la façon dont vous déclarez vos fonctions. var f = function(){ ... }implique que vous souhaitez que cela change à un moment donné, ce qui n'est probablement pas votre intention. Utilisez function name(){ ... }plutôt la syntaxe conventionnelle .
Braden Best
Pour démontrer tous ces points critiques, voici une version améliorée de la fonction dans votre réponse.
Braden Best
2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);
yckart
la source
1

Pour ceux qui ont été redirigés ici de Passer un nombre variable d'arguments d'une fonction à une autre (qui ne doit pas être marqué comme un double de cette question):

Si vous essayez de passer un nombre variable d'arguments d'une fonction à une autre, depuis JavaScript 1.8.5, vous pouvez simplement appeler apply()la deuxième fonction et passer le argumentsparamètre:

var caller = function()
{
    callee.apply( null, arguments );
}

var callee = function()
{
    alert( arguments.length );
}

caller( "Hello", "World!", 88 ); // Shows "3".

Remarque: Le premier argument est le thisparamètre à utiliser. Passer nullappellera la fonction à partir du contexte global, c'est-à-dire comme une fonction globale au lieu de la méthode d'un objet.

Selon ce document , la spécification ECMAScript 5 a redéfini la apply()méthode pour prendre n'importe quel "objet générique de type tableau", au lieu de strictement un tableau. Ainsi, vous pouvez passer directement la argumentsliste dans la deuxième fonction.

Testé dans Chrome 28.0, Safari 6.0.5 et IE 10. Essayez-le avec ce JSFiddle .

andrewtc
la source
0

Oui, vous pouvez passer la variable no. d'arguments à une fonction. Vous pouvez utiliser applypour y parvenir.

Par exemple:

var arr = ["Hi","How","are","you"];

function applyTest(){
    var arg = arguments.length;
    console.log(arg);
}

applyTest.apply(null,arr);
Abhijit
la source
0

Voulez-vous que votre fonction réagisse à un argument de tableau ou à des arguments de variable? Dans ce dernier cas, essayez:

var func = function(...rest) {
  alert(rest.length);

  // In JS, don't use for..in with arrays
  // use for..of that consumes array's pre-defined iterator
  // or a more functional approach
  rest.forEach((v) => console.log(v));
};

Mais si vous souhaitez gérer un argument de tableau

var fn = function(arr) {
  alert(arr.length);

  for(var i of arr) {
    console.log(i);
  }
};
Dev Yego
la source