JavaScript zmienna liczba argumentów do działania

531

Czy istnieje sposób na zezwolenie na „nieograniczoną” zmienną dla funkcji w JavaScript?

Przykład:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Timo
źródło
powiązany / możliwy duplikat stackoverflow.com/questions/4633125/...
Luke
1
@Luke nie, nie jest. To pytanie dotyczy sposobu wywoływania funkcji z dowolną liczbą argumentów z argumentami w tablicy. To pyta, jak obsłużyć takie połączenie.
Scruffy,
1
Dla łatwiejszego wyszukiwania taka funkcja nazywa się „funkcją wariadyczną”.
gschenk

Odpowiedzi:

814

Jasne, po prostu użyj argumentsobiektu.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
Roufamatic
źródło
1
Tnx. Doskonale nadaje się do analizowania ciągów znaków od natywnego kodu Androida do javascript w widoku sieci Web.
Johan Hoeksma
4
To rozwiązanie działało dla mnie najlepiej. Dzięki. Więcej informacji na temat słowa kluczowego argumenty TUTAJ .
Użytkownik2
26
argumentsjest specjalnym obiektem „podobnym do tablicy”, co oznacza, że ​​ma długość, ale nie ma innych funkcji tablicy. Aby uzyskać więcej informacji, zobacz developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ... i ta odpowiedź: stackoverflow.com/a/13145228/1766230
Łukasz
4
Co ciekawe, metody Array w Javascript zostały zdefiniowane w taki sposób, że działają na każdym obiekcie, który ma lengthwłaściwość. Obejmuje to argumentsobiekt. Wiedząc o tym i że metoda concatzwraca kopię na tablicy „” To się nazywa dalej, możemy łatwo przekształcić argumentsobiekt do prawdziwej tablicy tak: var args = [].concat.call(arguments). Niektórzy wolą używać Array.prototype.concat.callzamiast tego, ale podoba mi się to [], że są krótkie i słodkie!
Stijn de Witt
2
@ YasirJan use [...arguments].join()
Willian D. Andrade
179

W (najnowszych) przeglądarkach możesz przyjmować zmienną liczbę argumentów za pomocą tej składni:

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

Oto mały przykład:

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

Dokumentacja i więcej przykładów tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Ramast
źródło
27
FYI nazywa się to „składnią pozostałych parametrów”: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k
4
+1 To eleganckie i czyste rozwiązanie. Szczególnie nadaje się do przekazywania długiej listy parametrów do innego wywołania funkcji i możliwe, że te zmienne parametry znajdują się w dowolnej pozycji.
haxpor
3
Należy pamiętać, że zgodnie z developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… nie jest obsługiwany w IE.
Roee Gavirel
2
Oto tabela pokazująca obsługę przeglądarki - caniuse.com/#feat=rest-parameters
Brian Burns
1
Niezła odpowiedź!
Ashish
113

Inną opcją jest przekazanie argumentów w obiekcie kontekstowym.

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

i użyj tego w ten sposób

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

Ma to tę zaletę, że można dodać dowolną liczbę nazwanych argumentów, a funkcja może ich używać (lub nie) według własnego uznania.

Rozpoznać
źródło
4
Byłoby lepiej, ponieważ usuwa sprzężenie z kolejnością argumentów. Luźno sprzężone interfejsy to dobra standardowa praktyka ...
Jonas Schubert Erlandsson
9
Jasne, w niektórych przypadkach tak jest lepiej. Powiedzmy jednak, że poszczególne argumenty tak naprawdę nie są ze sobą powiązane lub wszystkie mają mieć takie samo znaczenie (jak elementy tablicy). Zatem sposób OP jest najlepszy.
rvighne
1
Jest to również miłe, ponieważ jeśli chcesz, możesz zbudować contextargument za pomocą kodu i przekazać go, zanim zostanie wykorzystany.
Nate CK
46

Zgadzam się z odpowiedzią Kena jako najbardziej dynamiczną i lubię iść o krok dalej. Jeśli jest to funkcja, którą wywołujesz wiele razy z różnymi argumentami - używam projektu Kena, ale następnie dodam wartości domyślne:

function load(context) {

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

    var context = extend(defaults, context);

    // do stuff
}

W ten sposób, jeśli masz wiele parametrów, ale niekoniecznie musisz je ustawiać przy każdym wywołaniu funkcji, możesz po prostu określić wartości domyślne. W przypadku metody ext możesz użyć metody jQuery's ext ( $.extend()), stworzyć własną lub zastosować następujące:

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

Spowoduje to scalenie obiektu kontekstu z wartościami domyślnymi i wypełnienie wszelkich niezdefiniowanych wartości w twoim obiekcie wartościami domyślnymi.

mbeasley
źródło
3
+1. Niezła sztuczka. Zapisuje dużo płyty kotła, aby zdefiniować każdy parametr, domyślny lub inny.
Neil
1
_.defaults()Metoda podkreślenia jest bardzo dobrą alternatywą do łączenia określonych i domyślnych argumentów.
mbeasley
18

Tak, po prostu tak:

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

load(1,2);
Canavar
źródło
18

Lepiej jest używać składni parametru rest, jak wskazał Ramast.

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

Chcę tylko dodać jakąś fajną właściwość argumentu ... args

  1. Jest to tablica, a nie obiekt podobny do argumentów. Umożliwia to bezpośrednie zastosowanie funkcji takich jak mapa lub sortowanie.
  2. Nie obejmuje wszystkich parametrów, ale tylko jeden przekazany z niego dalej. Np. Funkcja (a, b, ... args) w tym przypadku args zawiera argument 3 do arguments.length
Nicola Pedretti
źródło
15

Jak już wspomniano, możesz użyć arguments obiektu do pobrania zmiennej liczby parametrów funkcji.

Jeśli chcesz wywołać inną funkcję z tymi samymi argumentami, użyj apply. Możesz nawet dodawać lub usuwać argumenty, konwertując argumentsna tablicę. Na przykład ta funkcja wstawia tekst przed zalogowaniem się do konsoli:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Peter Tseng
źródło
fajne rozwiązanie do konwersji argumentów na tablicę. To mi dzisiaj pomogło.
Dmytro Medvid
8

Chociaż ogólnie zgadzam się, że metoda nazwanych argumentów jest użyteczna i elastyczna (chyba że zależy ci na kolejności, w którym to przypadku argumenty są najłatwiejsze), mam obawy co do kosztu podejścia mbeasley (używanie domyślnych i rozszerzeń). Jest to ogromna kwota, jaką trzeba wziąć za wyciągnięcie wartości domyślnych. Po pierwsze, wartości domyślne są zdefiniowane w funkcji, więc są ponownie wypełniane przy każdym wywołaniu. Po drugie, możesz łatwo odczytać nazwane wartości i jednocześnie ustawić wartości domyślne za pomocą ||. Nie ma potrzeby tworzenia ani łączenia kolejnego nowego obiektu, aby uzyskać te informacje.

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

   // do stuff
}

Jest to w przybliżeniu ta sama ilość kodu (może nieco więcej), ale powinna stanowić ułamek kosztów środowiska wykonawczego.

mcurland
źródło
Uzgodnione, chociaż szkoda zależy od rodzaju wartości lub samego niewykonania zobowiązania. W przeciwnym razie (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)lub dla mniejszej objętości kodu mała funkcja pomocnicza, taka jak: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)zapewnia alternatywne wzorce. Jednak mój punkt widzenia jest nadal słuszny: tworzenie dodatkowych obiektów dla każdego wywołania funkcji i uruchamianie drogich pętli w celu ustawienia kilku wartości domyślnych jest szalonym nakładem pracy.
mcurland
7

Podczas gdy @roufamatic pokazał użycie słowa kluczowego arguments, a @Ken pokazał świetny przykład obiektu do użycia, nie wydaje mi się, aby naprawdę zajął się tym, co dzieje się w tym przypadku i może dezorientować przyszłych czytelników lub zaszczepić złą praktykę, ponieważ nie określa wyraźnie funkcji Metoda / ma na celu przyjmowanie zmiennej liczby argumentów / parametrów.

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

Gdy inny programista przegląda Twój kod, bardzo łatwo jest założyć, że ta funkcja nie przyjmuje parametrów. Zwłaszcza jeśli programista nie jest wtajemniczony w słowo kluczowe arguments . Z tego powodu dobrym pomysłem jest przestrzeganie wytycznych dotyczących stylu i zachowanie spójności. Będę używać Google do wszystkich przykładów.

Powiedzmy wprost, że ta sama funkcja ma zmienne parametry:

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

Parametr obiektowy VS var_args

Może się zdarzyć, że obiekt jest potrzebny, ponieważ jest to jedyna zatwierdzona i uważana metoda najlepszej praktyki mapy danych. Tablice asocjacyjne są odrzucone i zniechęcone.

SIDENOTE: Słowo kluczowe argumenty faktycznie zwraca obiekt za pomocą liczb jako klucza. Dziedziczenie prototypowe jest także rodziną obiektów. Zobacz koniec odpowiedzi dla właściwego użycia tablicy w JS

W tym przypadku możemy to również wyraźnie stwierdzić. Uwaga: ta konwencja nazewnictwa nie jest dostarczana przez Google, ale jest przykładem wyraźnej deklaracji typu parametru. Jest to ważne, jeśli chcesz utworzyć bardziej ścisły wzór w kodzie.

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

Który wybrać?

To zależy od potrzeb twojej funkcji i programu. Jeśli na przykład chcesz zwrócić wartość na podstawie iteracyjnego procesu dla wszystkich przekazanych argumentów, z pewnością trzymaj się słowa kluczowego arguments . Jeśli potrzebujesz definicji argumentów i mapowania danych, wybierz metodę obiektową. Spójrzmy na dwa przykłady i gotowe!

Wykorzystanie argumentów

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

Wykorzystanie obiektu

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

Dostęp do tablicy zamiast obiektu („... args” Parametr rest)

Jak wspomniano na górze odpowiedzi, słowo kluczowe argumenty faktycznie zwraca obiekt. Z tego powodu należy wywołać dowolną metodę, której chcesz użyć dla tablicy. Przykład tego:

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

Aby tego uniknąć, użyj parametru rest:

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Brian Ellis
źródło
5

Użyj argumentsobiektu znajdującego się w funkcji, aby mieć dostęp do wszystkich przekazanych argumentów.

nickytonline
źródło
3

Należy pamiętać, że przekazanie obiektu o nazwanych właściwościach, jak sugeruje Ken, zwiększa koszt przydzielania i zwalniania obiektu tymczasowego do każdego wywołania. Przekazywanie normalnych argumentów przez wartość lub odwołanie będzie na ogół najbardziej wydajne. Jednak w wielu aplikacjach wydajność nie jest krytyczna, ale w niektórych przypadkach może być.

phbcanada
źródło