Czy można wysłać zmienną liczbę argumentów do funkcji JavaScript?

171

Czy jest możliwe wysłanie zmiennej liczby argumentów do funkcji JavaScript z tablicy?

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'

Niedawno napisałem dużo Pythona i to wspaniały wzorzec, aby móc akceptować varargi i je wysyłać. na przykład

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'

Czy w JavaScript można wysłać tablicę, która ma być traktowana jako tablica argumentów?

Fire Crow
źródło
4
Zauważ, że nie jest dobrym pomysłem używanie for - inpętli z argumentsobiektem - lengthzamiast tego należy użyć „normalnej” pętli for iterującej po właściwości
Yi Jiang
2
to nigdy nie było problemem, czy możesz wyjaśnić, dlaczego tak jest? obiekt arguments jest prawie zawsze dostatecznie mały, aby uzyskać pomijalną poprawę wydajności przy używaniu wersji agruments [0..length-1].
Fire Crow
Możliwy duplikat zmiennej JavaScript z liczbą argumentów funkcji
Anderson Green
1
Myślę, że argumenty nie są rzeczywistą tablicą, ale specjalnym obiektem, więc składnia „in” może nie działać, w zależności od implementacji JS.
O'Rooney

Odpowiedzi:

208

Aktualizacja : od wersji ES6 możesz po prostu użyć składni spreadu podczas wywoływania funkcji:

func(...arr);

Ponieważ ES6 również, jeśli spodziewasz się traktować swoje argumenty jako tablicę, możesz również użyć składni rozkładów na liście parametrów, na przykład:

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

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

I możesz to połączyć z normalnymi parametrami, na przykład jeśli chcesz otrzymać dwa pierwsze argumenty osobno, a resztę jako tablicę:

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

A może przydaje się to, że wiesz, ile argumentów oczekuje funkcja:

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

Ale w każdym razie możesz przekazać dowolną liczbę argumentów ...

Składnia spreadu jest krótsza i „słodsza” niż, applya jeśli nie musisz ustawiać thiswartości w wywołaniu funkcji, to jest właściwy sposób.

Oto przykład zastosowania , który był wcześniejszym sposobem:

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

Obecnie zalecam używanie applytylko wtedy, gdy musisz przekazać dowolną liczbę argumentów z tablicy i ustawić thiswartość. applytakes to thiswartość jako pierwsze argumenty, które zostaną użyte przy wywołaniu funkcji, jeśli używamy nullw kodzie nieścisłym, thissłowo kluczowe będzie odnosić się do obiektu Global (okna) wewnątrz func, w trybie ścisłym, gdy jawnie użyjemy 'use strict 'lub w modułach ES null.

Zauważ również, że argumentsobiekt nie jest tak naprawdę tablicą, możesz go przekonwertować przez:

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

A w ES6:

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

Ale obecnie rzadko używasz tego argumentsobiektu bezpośrednio dzięki składni rozprzestrzeniania.

CMS
źródło
1
Dzięki, zastosuj załatwia sprawę, zadzwoń w tym przypadku nie działa. czy to było napisane przez pomyłkę?
Fire Crow
Jak to zrobić, jeśli funkcja jest w rzeczywistości członkiem klasy? Czy to jest poprawne? theObject.member.apply (theObject, arguments);
Baxissimo
4
Array.prototype.slice.call(arguments);zapobiega optymalizacji JS przeglądarki, takiej jak V8. Szczegóły tutaj
Dheeraj Bhaskar
2
1. Najwyraźniej argumentsplanuje się wycofać na korzyść operatora spreadu . Nie mam na to źródła, ale gdzieś to słyszałem. 2. MDN stwierdza, że użycie slicena argumentsobiekcie zapobiega optymalizacji w silnikach takich jak V8. Sugerują użycie „pogardzanego konstruktora Array” Array.from(arguments), lub [...arguments]. Czy zechciałbyś zaktualizować swoją odpowiedź dotyczącą ES2015?
Braden Best
NB: Poszedłem dalej i napisałem własną odpowiedź .
Braden Best
75

W rzeczywistości możesz przekazać dowolną liczbę wartości do dowolnej funkcji javascript. Jawnie nazwane parametry otrzymają kilka pierwszych wartości, ale WSZYSTKIE parametry zostaną zapisane w tablicy argumentów.

Aby przekazać tablicę argumentów w formie „rozpakowanej”, możesz użyć Apply, tak jak w przypadku (patrz Funkcjonalny JavaScript ):

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
źródło
23

Operatory splat i spread są częścią ES6, planowanej następnej wersji Javascript. Jak dotąd tylko Firefox je obsługuje. Ten kod działa w 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');

Zwróć uwagę na składnię awkard dla rozprzestrzeniania. Zwykła składnia niesprintf('The %s %s fox jumps over the %s dog.', ...arr); jest jeszcze obsługiwana . Tabelę kompatybilności ES6 znajdziesz tutaj .

Zwróć również uwagę na użycie for...ofinnego dodatku ES6. Używanie for...indla tablic to zły pomysł .

Tgr
źródło
Normalna składnia rozpowszechniania jest teraz obsługiwana w przeglądarkach Firefox i Chrome
użytkownik
8

applyFunkcja przyjmuje dwa argumenty; obiekt thiszostanie powiązany, a argumenty reprezentowane przez tablicę.

some_func = function (a, b) { return b }
some_func.apply(obj, ["arguments", "are", "here"])
// "are"
August Lilleaas
źródło
8

Od ES2015 istnieją dwie metody.

argumentsobiektu

Ten obiekt jest wbudowany w funkcje i odpowiednio odwołuje się do argumentów funkcji. Jednak technicznie nie jest to tablica, więc typowe operacje tablicowe nie będą na niej działać. Sugerowaną metodą jest użycie Array.fromlub operatora spreadu w celu utworzenia z niego tablicy.

Widziałem inne odpowiedzi, w których wspominano o użyciu slice. Nie rób tego. Zapobiega optymalizacjom (źródło: MDN ).

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

Jednak uważam, że argumentsjest to problematyczne, ponieważ ukrywa to, co funkcja przyjmuje jako dane wejściowe. argumentsFunkcja zwykle jest napisane tak:

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

Czasami nagłówek funkcji jest zapisywany w następujący sposób, aby udokumentować argumenty w sposób podobny do C:

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

Ale to rzadkie.

Jeśli chodzi o powody, dla których jest to problematyczne, weźmy na przykład C. C jest wstecznie kompatybilny ze starożytnym dialektem języka sprzed ANSI znanym jako K&R C. K&R C pozwala prototypom funkcji mieć pustą listę argumentów.

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

ANSI C udostępnia funkcję varargs( ...) i voidokreśla pustą listę argumentów.

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

Wiele osób nieumyślnie deklaruje funkcje z unspecifiedlistą argumentów ( int myfunction();), gdy oczekują, że funkcja przyjmuje zero argumentów. Jest to technicznie to błąd, ponieważ funkcja będzie akceptować argumenty. Dowolna ich liczba.

Właściwa varargsfunkcja w C ma postać:

int myFunction(int nargs, ...);

A JavaScript faktycznie ma coś podobnego.

Parametry operatora / odpoczynku

Właściwie już pokazałem wam operator spreadu.

...name

Jest dość wszechstronny i może być również użyty na liście argumentów funkcji ("parametry reszty"), aby określić varargs w ładnie udokumentowany sposób:

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

Lub jako wyrażenie lambda:

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

O wiele bardziej wolę operatora rozprzestrzeniania. Jest czysty i samodokumentujący.

Braden Best
źródło
2
dziękuję za zaktualizowaną odpowiedź, ten post pochodzi sprzed ES 2015, więc cieszę się, że wprowadziłeś nowsze opcje
Fire Crow
5

Nazywa się splatoperatorem. Możesz to zrobić w JavaScript używając 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
źródło
4

W ES6 możesz użyć parametrów reszt dla varagrs. Pobiera listę argumentów i konwertuje ją na tablicę.

function logArgs(...args) {
    console.log(args.length)
    for(let arg of args) {
        console.log(arg)
    }
}
theres.yer.problem
źródło
2

To jest przykładowy program do obliczania sumy liczb całkowitych dla zmiennych argumentów i tablicy na liczbach całkowitych. Mam nadzieję że to pomoże.

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);
smuga
źródło
1
Kilka uwag: 1. vardeklaruje function scopezmienną. Nowe leti constsłowa kluczowe (ES 2015) deklarują block scopezmienne. Jednak przed ES 2015 zmienne należało nadal podnosić na szczyt funkcji w ramach praktyki. 2. przerw funkcyjne gdy podaje się 1 wartość całkowitą z powodu swojej błędnej akceptacji tablicę jako argument (pytanie pyta o varargs; nie akceptując tablic jako argumentów) CalculateSum(6) -> 0. (kontynuacja w następnym poście)
Braden Best
1
3. Nie używaj funkcji debugowania, takich jak alert. Właściwie, po prostu nie używaj alert/prompt/confirm, kropka. Użyj console.logzamiast tego podczas debugowania. 4. Funkcja powinna być returnwartością, a nie alertnią. 5. Unikaj, PascalCasechyba że odnosi się to do „typu” danych. To znaczy klasa. Użyj camelCaselub under_scorezamiast. 4. Nie podoba mi się sposób, w jaki deklarujesz swoje funkcje. var f = function(){ ... }oznacza, że ​​chcesz, aby w pewnym momencie się to zmieniło , co prawdopodobnie nie jest Twoim zamiarem. function name(){ ... }Zamiast tego użyj konwencjonalnej składni.
Braden Best
Aby zademonstrować wszystkie te punkty krytyczne, oto ulepszona wersja funkcji w Twojej odpowiedzi.
Braden Best
2
(function(window) {
  var proxied = window.alert;
  window.alert = function() {
    return proxied.apply(window, arguments);
  };
}(this));

alert(13, 37);
yckart
źródło
1

Dla tych, którzy zostali tutaj przekierowani z Przekazywanie zmiennej liczby argumentów z jednej funkcji do drugiej (co nie powinno być oznaczane jako duplikat tego pytania):

Jeśli próbujesz przekazać zmienną liczbę argumentów z jednej funkcji do drugiej, od czasu JavaScript 1.8.5 możesz po prostu wywołać apply()drugą funkcję i przekazać argumentsparametr:

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

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

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

Uwaga: pierwszym argumentem jest thisparametr, którego należy użyć. Przekazanie nullwywoła funkcję z kontekstu globalnego, tj. Jako funkcję globalną zamiast metody jakiegoś obiektu.

Zgodnie z tym dokumentem specyfikacja ECMAScript 5 na nowo zdefiniowała apply()metodę tak, aby przyjmowała dowolny „ogólny obiekt przypominający tablicę” zamiast wyłącznie tablicy. W ten sposób możesz bezpośrednio przekazać argumentslistę do drugiej funkcji.

Testowane w Chrome 28.0, Safari 6.0.5 i IE 10. Wypróbuj to z tym JSFiddle .

andrewtc
źródło
0

Tak, możesz przekazać zmienną nie. argumentów do funkcji. Możesz użyć, applyaby to osiągnąć.

Na przykład:

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

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

applyTest.apply(null,arr);
Abhijit
źródło
0

Czy chcesz, aby funkcja reagowała na argument tablicy lub argumenty zmiennych? Jeśli to drugie, spróbuj:

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

Ale jeśli chcesz obsłużyć argument tablicy

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

  for(var i of arr) {
    console.log(i);
  }
};
Dev Yego
źródło