Jumlah variabel JavaScript untuk berfungsi

531

Apakah ada cara untuk mengizinkan vars "tidak terbatas" untuk suatu fungsi dalam JavaScript?

Contoh:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
Timo
sumber
terkait / kemungkinan duplikat stackoverflow.com/questions/4633125/…
Luke
1
@ Lukas tidak, tidak. Pertanyaan itu menanyakan bagaimana memanggil suatu fungsi dengan sejumlah argumen sembarang dengan argumen dalam sebuah array. Ini bertanya bagaimana menangani panggilan seperti itu.
Berantakan
1
Untuk pencarian yang lebih mudah, fungsi seperti itu disebut 'fungsi variadic'.
gschenk

Jawaban:

814

Tentu, cukup gunakan argumentsobjeknya.

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
roufamatic
sumber
1
Tnx. Ini bagus untuk parsing String dari kode asli Android ke javascript di Webview.
Johan Hoeksma
4
Solusi ini bekerja paling baik untuk saya. Terima kasih. Informasi lebih lanjut tentang argumen kata kunci DI SINI .
User2
26
argumentsadalah objek khusus "Array-like", yang berarti memiliki panjang, tetapi tidak ada fungsi array lainnya. Lihat developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… untuk informasi lebih lanjut, dan jawaban ini: stackoverflow.com/a/13145228/1766230
Luke
4
Menariknya, metode Array dalam Javascript telah didefinisikan sedemikian rupa sehingga mereka bekerja pada objek apa pun yang memiliki lengthproperti. Ini termasuk argumentsobjek. Mengetahui hal ini, dan bahwa metode concatmengembalikan salinan dari 'array yang' itu disebut, kita dapat dengan mudah mengkonversi argumentsobjek untuk array nyata seperti ini: var args = [].concat.call(arguments). Beberapa orang lebih suka menggunakan Array.prototype.concat.call, tetapi saya suka [], mereka pendek dan manis!
Stijn de Witt
2
@YasirJan digunakan[...arguments].join()
Willian D. Andrade
179

Di (sebagian besar) browser terbaru, Anda dapat menerima jumlah variabel argumen dengan sintaks ini:

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

Ini sebuah contoh kecil:

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

Dokumentasi dan lebih banyak contoh di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Ramast
sumber
27
FYI itu disebut "sintaks parameter sisanya": developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
tanguy_k
4
+1 Ini adalah solusi yang elegan dan bersih. Terutama cocok untuk melewati daftar panjang parameter ke pemanggilan fungsi lain, dan dengan kemungkinan bahwa parameter variabel berada pada posisi sewenang-wenang.
haxpor
3
Perlu diingat bahwa menurut developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… itu tidak didukung di IE.
Roee Gavirel
2
Berikut adalah tabel yang menunjukkan dukungan browser - caniuse.com/#feat=rest-parameters
Brian Burns
1
Jawaban yang sangat bagus!
Ashish
113

Pilihan lain adalah meneruskan argumen Anda dalam objek konteks.

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

dan gunakan seperti ini

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

Ini memiliki keuntungan bahwa Anda dapat menambahkan argumen bernama sebanyak yang Anda inginkan, dan fungsinya dapat menggunakannya (atau tidak) sesuai keinginan.

Ken
sumber
4
Ini akan lebih baik karena menghapus sambungan ke urutan argumen. Antarmuka yang longgar ditambah adalah praktik standar yang baik ...
Jonas Schubert Erlandsson
9
Tentu, itu lebih baik dalam beberapa kasus. Tetapi katakanlah argumen individu tidak benar-benar berhubungan satu sama lain, atau semua seharusnya memiliki makna yang sama (seperti elemen array). Maka cara OP adalah yang terbaik.
rvighne
1
Ini juga bagus karena jika Anda mau, Anda bisa membangun contextargumen dengan kode dan menyebarkannya sebelum digunakan.
Nate CK
46

Saya setuju dengan jawaban Ken sebagai yang paling dinamis dan saya ingin melangkah lebih jauh. Jika fungsi yang Anda panggil beberapa kali dengan argumen yang berbeda - Saya menggunakan desain Ken tetapi kemudian menambahkan nilai default:

function load(context) {

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

    var context = extend(defaults, context);

    // do stuff
}

Dengan cara ini, jika Anda memiliki banyak parameter tetapi tidak perlu mengaturnya dengan setiap panggilan ke fungsi, Anda cukup menentukan non-default. Untuk metode perluasan, Anda dapat menggunakan metode perluasan jQuery ( $.extend()), membuat sendiri atau menggunakan yang berikut:

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

Ini akan menggabungkan objek konteks dengan default dan mengisi nilai yang tidak terdefinisi dalam objek Anda dengan default.

mbeasley
sumber
3
+1. Trik yang bagus. Menghemat banyak pelat ketel untuk menentukan setiap parameter, default atau lainnya.
Neil
1
_.defaults()Metode Underscore adalah alternatif yang sangat bagus untuk menggabungkan argumen yang ditentukan dan default.
mbeasley
18

Ya persis seperti ini:

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

load(1,2);
Canavar
sumber
18

Lebih baik menggunakan sintaks parameter istirahat seperti yang ditunjukkan Ramast.

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

Saya hanya ingin menambahkan beberapa properti bagus dari ... argumen args

  1. Ini adalah array, dan bukan objek seperti argumen. Ini memungkinkan Anda untuk menerapkan fungsi seperti peta atau mengurutkan secara langsung.
  2. Itu tidak termasuk semua parameter tetapi hanya satu yang diteruskan dari itu. Misalnya fungsi (a, b, ... args) dalam hal ini args berisi argumen 3 ke arguments.length
Nicola Pedretti
sumber
15

Seperti yang sudah disebutkan, Anda dapat menggunakan argumentsobjek untuk mengambil sejumlah variabel parameter fungsi.

Jika Anda ingin memanggil fungsi lain dengan argumen yang sama, gunakan apply. Anda bahkan dapat menambah atau menghapus argumen dengan mengonversikannya argumentske array. Misalnya, fungsi ini menyisipkan beberapa teks sebelum masuk ke konsol:

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
Peter Tseng
sumber
solusi bagus untuk mengonversi argumen menjadi array. Itu sangat membantu saya hari ini.
Dmytro Medvid
8

Meskipun saya umumnya setuju bahwa pendekatan argumen yang disebutkan berguna dan fleksibel (kecuali jika Anda peduli tentang urutannya, dalam hal mana argumen paling mudah), saya memang khawatir tentang biaya pendekatan mbeasley (menggunakan default dan extends). Ini adalah jumlah biaya ekstrem yang harus diambil untuk menarik nilai default. Pertama, default didefinisikan di dalam fungsi, sehingga dihuni kembali pada setiap panggilan. Kedua, Anda dapat dengan mudah membaca nilai yang disebutkan dan mengatur default pada saat yang sama menggunakan ||. Tidak perlu lagi membuat dan menggabungkan objek baru untuk mendapatkan informasi ini.

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

   // do stuff
}

Ini kira-kira jumlah kode yang sama (mungkin sedikit lebih), tetapi harus menjadi sebagian kecil dari biaya runtime.

mcurland
sumber
Setuju, meskipun kerugiannya tergantung pada jenis nilai atau standar itu sendiri. Jika tidak, (parameter1=context.parameter1)===undefined && (parameter1=defaultValue1)atau untuk volume kode yang lebih sedikit, fungsi pembantu kecil seperti: function def(providedValue, default) {return providedValue !== undefined ? providedValue : defaultValue;} var parameter1 = def(context.parameter1, defaultValue1)memberikan pola alternatif. Namun, maksud saya masih berdiri: membuat objek tambahan untuk setiap pemanggilan fungsi dan menjalankan loop mahal untuk menetapkan beberapa nilai default adalah jumlah overhead gila.
mcurland
7

Sementara @roufamatic memang menunjukkan penggunaan kata kunci argumen dan @Ken menunjukkan contoh objek yang bagus untuk digunakan, saya merasa tidak benar-benar membahas apa yang terjadi dalam hal ini dan dapat membingungkan pembaca di masa depan atau menanamkan praktik buruk karena tidak secara eksplisit menyatakan fungsi. / Metode dimaksudkan untuk mengambil jumlah variabel argumen / parameter.

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

Ketika pengembang lain melihat melalui kode Anda, sangat mudah untuk menganggap fungsi ini tidak mengambil parameter. Apalagi jika pengembang itu tidak mengetahui argumen kata kunci . Karena itu, ide yang baik untuk mengikuti pedoman gaya dan konsisten. Saya akan menggunakan Google untuk semua contoh.

Mari kita secara eksplisit menyatakan fungsi yang sama memiliki parameter variabel:

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

Parameter objek VS var_args

Mungkin ada saat-saat ketika suatu objek dibutuhkan karena merupakan satu-satunya metode praktik terbaik dari peta data. Array asosiatif disukai dan tidak dianjurkan.

SIDENOTE: Kata kunci argumen sebenarnya mengembalikan kembali suatu objek menggunakan angka sebagai kunci. Warisan prototypal juga merupakan objek keluarga. Lihat akhir jawaban untuk penggunaan array yang tepat di JS

Dalam hal ini kita dapat secara eksplisit menyatakan ini juga. Catatan: konvensi penamaan ini tidak disediakan oleh Google tetapi merupakan contoh deklarasi eksplisit dari jenis param. Ini penting jika Anda ingin membuat pola pengetikan yang lebih ketat dalam kode Anda.

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

Yang mana yang harus dipilih?

Ini tergantung pada kebutuhan fungsi dan program Anda. Jika misalnya Anda hanya ingin mengembalikan basis nilai pada proses berulang di semua argumen yang disahkan maka pasti tetap dengan kata kunci argumen . Jika Anda membutuhkan definisi untuk argumen dan pemetaan data Anda maka metode objek adalah cara yang harus dilakukan. Mari kita lihat dua contoh dan kemudian kita selesai!

Penggunaan argumen

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

Penggunaan objek

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

Mengakses array daripada objek ("... args" Parameter lainnya)

Seperti disebutkan di bagian atas jawaban, kata kunci argumen sebenarnya mengembalikan sebuah objek. Karena ini, metode apa pun yang ingin Anda gunakan untuk sebuah array harus dipanggil. Contoh dari ini:

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

Untuk menghindari hal ini, gunakan parameter lainnya:

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

Gunakan argumentsobjek ketika di dalam fungsi untuk memiliki akses ke semua argumen yang dilewatkan.

nickytonline
sumber
3

Ketahuilah bahwa melewatkan Objek dengan properti yang dinamai Ken menyarankan menambah biaya pengalokasian dan melepaskan objek sementara untuk setiap panggilan. Melewati argumen normal dengan nilai atau referensi umumnya akan menjadi yang paling efisien. Untuk banyak aplikasi meskipun kinerjanya tidak kritis tetapi untuk beberapa hal itu bisa saja.

phbcanada
sumber