È possibile inviare un numero variabile di argomenti a una funzione JavaScript?


171

È possibile inviare un numero variabile di argomenti a una funzione JavaScript, da un array?

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'

Di recente ho scritto un sacco di Python ed è un modello meraviglioso poter accettare i vararg e inviarli. per esempio

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'

In JavaScript è possibile inviare un array da trattare come array di argomenti?


4
Si noti che non è una buona idea usare un for - inloop con l' argumentsoggetto - si lengthdovrebbe usare invece un 'normale' per loop che scorre la proprietà
Yi Jiang

2
non è mai stato un problema, puoi spiegare perché è così? L'oggetto argomenti è quasi sempre abbastanza piccolo da avere un miglioramento trascurabile delle prestazioni per l'utilizzo della versione agruments [0..length-1].
Fire Crow


1
Penso che gli argomenti non siano un array reale, ma un oggetto speciale, quindi la sintassi "in" potrebbe non funzionare, a seconda dell'implementazione di JS.
O'Rooney,

Risposte:


209

Aggiornamento : da ES6, puoi semplicemente utilizzare la sintassi di diffusione quando chiami la funzione:

func(...arr);

Dal momento che ES6 anche se prevedi di trattare i tuoi argomenti come un array, puoi anche utilizzare la sintassi di diffusione nell'elenco dei parametri, ad esempio:

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

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

E puoi combinarlo con parametri normali, ad esempio se vuoi ricevere i primi due argomenti separatamente e il resto come un array:

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

E forse ti è utile sapere che puoi aspettarti quanti argomenti si aspetta una funzione:

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

Ma comunque puoi passare un numero arbitrario di argomenti ...

La sintassi di diffusione è più breve e "più dolce" di applye se non è necessario impostare il thisvalore nella chiamata di funzione, questa è la strada da percorrere.

Ecco un esempio di applicazione , che era il primo modo per farlo:

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

Al giorno d'oggi raccomando di utilizzare applysolo se è necessario passare un numero arbitrario di argomenti da un array e impostare il thisvalore. applyprende il thisvalore come primo argomento, che verrà usato nell'invocazione della funzione, se usiamo nullin codice non rigoroso, la thisparola chiave si riferirà all'oggetto Globale (finestra) all'interno func, in modalità rigorosa, quando usa esplicitamente 'usa rigoroso 'o nei moduli ES, nullverranno utilizzati.

Si noti inoltre che l' argumentsoggetto non è in realtà un array, è possibile convertirlo mediante:

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

E in ES6:

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

Ma raramente usi l' argumentsoggetto direttamente al giorno d'oggi grazie alla sintassi diffusa.


1
Grazie, applicare fa il trucco, in questo caso la chiamata non funziona. è stato scritto per errore?
Fire Crow,

Come si fa se la funzione è effettivamente un membro di una classe? È giusto? theObject.member.apply (theObject, argomenti);
Baxissimo,

4
Array.prototype.slice.call(arguments);impedisce le ottimizzazioni JS del browser come V8. Dettagli qui
Dheeraj Bhaskar,

2
1. Apparentemente, argumentsè previsto un deprezzamento a favore dell'operatore di spread . Non ho una fonte per questo, ma l'ho sentito da qualche parte. 2. MDN afferma che l'utilizzo slicesulle argumentsoggetto impedisce l'ottimizzazione nei motori come V8. Suggeriscono di usare il "costruttore di array disprezzato" Array.from(arguments), oppure [...arguments]. Ti andrebbe di aggiornare la tua risposta per ES2015?
Braden Best,

NB: sono andato avanti e ho scritto la mia risposta .
Braden Best,

75

Puoi effettivamente passare tutti i valori che vuoi a qualsiasi funzione javascript. I parametri con nome esplicito otterranno i primi pochi valori, ma TUTTI i parametri verranno archiviati nella matrice degli argomenti.

Per passare l'array degli argomenti in formato "decompresso", è possibile utilizzare apply, in questo modo (vedi Javascript funzionale ):

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

23

Gli operatori splat e spread fanno parte di ES6, la prossima versione pianificata di Javascript. Finora solo Firefox li supporta. Questo codice funziona in 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');

Nota la sintassi di awkard per spread. La solita sintassi di nonsprintf('The %s %s fox jumps over the %s dog.', ...arr); è ancora supportata . È possibile trovare una tabella di compatibilità ES6 qui .

Nota anche l'uso di for...ofun'altra aggiunta ES6. L'uso for...indi array è una cattiva idea .


La sintassi normale per la diffusione è ora supportata in Firefox e Chrome
utente

8

La applyfunzione accetta due argomenti; l'oggetto thisverrà associato e gli argomenti, rappresentati con un array.

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

8

Esistono due metodi a partire da ES2015.

L' argumentsoggetto

Questo oggetto è incorporato in funzioni e fa riferimento, in modo appropriato, agli argomenti di una funzione. Tecnicamente non è un array, quindi le operazioni tipiche dell'array non funzionano su di esso. Il metodo suggerito è utilizzare Array.fromo l'operatore spread per creare un array da esso.

Ho visto altre risposte menzionate usando slice. Non farlo. Previene le ottimizzazioni (fonte: MDN ).

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

Tuttavia, direi che argumentsè problematico perché nasconde ciò che una funzione accetta come input. Una argumentsfunzione in genere è scritta come questa:

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

A volte, l'intestazione della funzione è scritta come la seguente per documentare gli argomenti in modo simile al C:

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

Ma è raro.

Per quanto riguarda il motivo per cui è problematico, prendi C, per esempio. C è retrocompatibile con un antico dialetto pre-ANSI del linguaggio noto come K&R C. K&R C consente ai prototipi di funzioni di avere un elenco di argomenti vuoto.

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

ANSI C fornisce una funzione per varargs( ...) e voidper specificare un elenco di argomenti vuoto.

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

Molte persone dichiarano inavvertitamente funzioni con un unspecifiedelenco di argomenti ( int myfunction();), quando si aspettano che la funzione prenda zero argomenti. Questo è tecnicamente un errore perché la funzione verrà accettare argomenti. Qualsiasi numero di loro.

Una varargsfunzione corretta in C assume la forma:

int myFunction(int nargs, ...);

E JavaScript in realtà ha qualcosa di simile a questo.

Diffondi operatore / Parametri di riposo

In realtà ti ho già mostrato l'operatore di diffusione.

...name

È piuttosto versatile e può anche essere usato nella lista di argomenti di una funzione ("parametri di riposo") per specificare i vararg in modo ben documentato:

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

O come espressione lambda:

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

Preferisco di gran lunga l'operatore di diffusione. È pulito e auto-documentato.


2
grazie per la risposta aggiornata, questo post precede ES 2015, quindi sono felice che tu abbia compilato le opzioni più recenti
Fire Crow,

5

Si chiama splatoperatore. Puoi farlo in JavaScript usando 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); 

4

Con ES6 è possibile utilizzare i parametri di riposo per varagrs. Questo prende l'elenco degli argomenti e lo converte in un array.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}

2

Questo è un programma di esempio per calcolare la somma di numeri interi per argomenti variabili e array su numeri interi. Spero che questo ti aiuti.

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

1
Alcune note: 1. vardichiara una function scopevariabile. Il nuovo lete le constparole chiave (ES 2015) dichiarano le block scopevariabili. Prima di ES 2015, tuttavia, le variabili avrebbero dovuto essere ancora sollevate al vertice della funzione come una questione di pratica. 2. La funzione pause quando somministrato 1 valore intero a causa di accettazione erronea di un array come argomento (la domanda riguarda varargs; non accettare array come argomenti): CalculateSum(6) -> 0. (continua nel prossimo post)
Braden Best

1
3. Non usare funzioni di debug come alert. In realtà, basta non usare alert/prompt/confirm, punto. Utilizzare console.loginvece durante il debug. 4. La funzione dovrebbe essere returnun valore, non un valore alert. 5. evitare a PascalCasemeno che non si riferisca a un "tipo" di dati. Cioè una classe. Usa camelCaseo under_scoreinvece. 4. Non mi piace il modo in cui dichiari le tue funzioni. var f = function(){ ... }implica che vuoi che cambi ad un certo punto, che probabilmente non è il tuo intento. Utilizzare function name(){ ... }invece la sintassi convenzionale .
Braden Best

Per dimostrare tutti questi punti di critica, ecco una versione migliorata della funzione nella tua risposta.
Braden Best

2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);

1

Per coloro che sono stati reindirizzati qui dal passaggio del numero variabile di argomenti da una funzione all'altra (che non dovrebbe essere contrassegnato come duplicato di questa domanda):

Se stai provando a passare un numero variabile di argomenti da una funzione all'altra, dal momento che JavaScript 1.8.5 puoi semplicemente chiamare apply()la seconda funzione e passare il argumentsparametro:

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

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

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

Nota: il primo argomento è il thisparametro da utilizzare. Il passaggio nullchiamerà la funzione dal contesto globale, ovvero come funzione globale anziché come metodo di un oggetto.

Secondo questo documento , la specifica ECMAScript 5 ha ridefinito il apply()metodo per prendere qualsiasi "oggetto generico simile ad un array", invece che rigorosamente un array. Pertanto, è possibile passare direttamente l' argumentselenco nella seconda funzione.

Testato su Chrome 28.0, Safari 6.0.5 e IE 10. Provalo con questo JSFiddle .


0

Sì, puoi passare la variabile no. di argomenti per una funzione. È possibile utilizzare applyper raggiungere questo obiettivo.

Per esempio:

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

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

applyTest.apply(null,arr);

0

Vuoi che la tua funzione reagisca a un argomento di matrice o argomenti variabili? Se quest'ultimo, prova:

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

Ma se si desidera gestire un argomento array

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

  for(var i of arr) {
    console.log(i);
  }
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.