Cara terbaik untuk menentukan apakah argumen tidak dikirim ke fungsi JavaScript

236

Saya sekarang telah melihat 2 metode untuk menentukan apakah suatu argumen telah diteruskan ke fungsi JavaScript. Saya bertanya-tanya apakah satu metode lebih baik dari yang lain atau apakah satu hanya buruk untuk digunakan?

 function Test(argument1, argument2) {
      if (Test.arguments.length == 1) argument2 = 'blah';

      alert(argument2);
 }

 Test('test');

Atau

 function Test(argument1, argument2) {
      argument2 = argument2 || 'blah';

      alert(argument2);
 }

 Test('test');

Sejauh yang saya tahu, keduanya menghasilkan hal yang sama, tetapi saya hanya menggunakan yang pertama sebelum produksi.

Opsi lain seperti yang disebutkan oleh Tom :

function Test(argument1, argument2) {
    if(argument2 === null) {
        argument2 = 'blah';
    }

    alert(argument2);
}

Sesuai komentar Juan, akan lebih baik untuk mengubah saran Tom menjadi:

function Test(argument1, argument2) {
    if(argument2 === undefined) {
        argument2 = 'blah';
    }

    alert(argument2);
}
Darryl Hein
sumber
Itu benar-benar sama. Jika Anda selalu memiliki jumlah argumen statis, lanjutkan dengan metode kedua, jika tidak, mungkin lebih mudah untuk beralih menggunakan array argumen.
Luca Matteis
7
Argumen yang tidak disahkan muncul sebagai tidak terdefinisi. Pengujian dengan kesetaraan ketat terhadap nol akan gagal. Anda harus menggunakan kesetaraan ketat dengan undefined.
Juan Mendes
19
Ingat: argument2 || 'blah';akan menghasilkan 'bla' jika argument2ini false, tidak hanya jika tidak terdefinisi (!). Jika argument2boolean, dan fungsi dilewatkan falseuntuk itu, baris itu akan mengembalikan 'bla' meskipun argument2telah didefinisikan dengan benar .
Sandy Gifford
9
@SandyGifford: Masalah yang sama jika argument2adalah 0, '', atau null.
rvighne
@rvighne Sangat benar. Interpretasi unik Javascript tentang objek dan casting sekaligus adalah bagian terbaik dan terburuk.
Sandy Gifford

Jawaban:

274

Ada beberapa cara berbeda untuk memeriksa apakah argumen dilewatkan ke suatu fungsi. Selain dua yang Anda sebutkan dalam pertanyaan (asli) Anda - memeriksa arguments.lengthatau menggunakan ||operator untuk memberikan nilai default - satu juga dapat secara eksplisit memeriksa argumen untuk undefinedmelalui argument2 === undefinedatau typeof argument2 === 'undefined'jika ada yang paranoid (lihat komentar).

Menggunakan ||Operator telah menjadi praktek standar - semua anak keren melakukannya - tapi hati-hati: Nilai default akan dipicu jika mengevaluasi argumen untuk false, yang berarti sebenarnya mungkin undefined, null, false, 0, ''(atau apa pun yang Boolean(...)kembali false).

Jadi pertanyaannya adalah kapan harus menggunakan cek mana, karena semuanya menghasilkan hasil yang sedikit berbeda.

Memeriksa arguments.lengthmenunjukkan perilaku 'paling benar', tetapi mungkin tidak layak jika ada lebih dari satu argumen opsional.

Tes untuk undefined'terbaik' berikutnya - itu hanya 'gagal' jika fungsi secara eksplisit disebut dengan undefinednilai, yang dalam semua kemungkinan harus diperlakukan dengan cara yang sama seperti menghilangkan argumen.

Penggunaan ||operator dapat memicu penggunaan nilai default bahkan jika argumen yang valid diberikan. Di sisi lain, perilakunya sebenarnya mungkin diinginkan.

Untuk meringkas: Hanya gunakan jika Anda tahu apa yang Anda lakukan!

Menurut pendapat saya, menggunakan ||juga merupakan cara untuk pergi jika ada lebih dari satu argumen opsional dan seseorang tidak ingin meneruskan objek literal sebagai solusi untuk parameter bernama.

Cara lain yang bagus untuk memberikan nilai-nilai default menggunakan arguments.lengthdimungkinkan dengan masuk ke label pernyataan switch:

function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
    switch(arguments.length) {
        case 1: optionalArg1 = 'default1';
        case 2: optionalArg2 = 'default2';
        case 3: optionalArg3 = 'default3';
        case 4: break;
        default: throw new Error('illegal argument count')
    }
    // do stuff
}

Ini memiliki kelemahan bahwa niat programmer tidak (secara visual) jelas dan menggunakan 'angka ajaib'; karena itu kemungkinan rawan kesalahan.

Christoph
sumber
42
Anda sebenarnya harus memeriksa typeof argument2 === "tidak terdefinisi", jika seseorang mendefinisikan "tidak terdefinisi".
JW.
133
Saya akan menambahkan pemberitahuan - tapi bajingan sakit apa yang melakukan hal seperti itu?
Christoph
12
undefined adalah variabel di ruang global. Itu lebih lambat untuk mencari variabel dalam lingkup global daripada variabel dalam lingkup lokal. Tetapi yang tercepat adalah menggunakan typeof x === "tidak terdefinisi"
beberapa
5
Menarik. Di sisi lain, perbandingan === tidak terdefinisi mungkin lebih cepat daripada perbandingan string. Tes saya tampaknya menunjukkan bahwa Anda benar, meskipun: x === tidak terdefinisi diperlukan ~ 1,5 kali waktu typeof x === 'tidak terdefinisi'
Christoph
4
@Cristoph: setelah membaca komentar Anda, saya bertanya-tanya. Saya dapat membuktikan bahwa perbandingan string jelas bukan (hanya) dengan perbandingan pointer karena membandingkan string raksasa membutuhkan waktu lebih lama daripada string kecil. Namun, membandingkan dua string raksasa benar-benar lambat hanya jika mereka dibangun dengan penggabungan, tetapi sangat cepat jika yang kedua dibuat sebagai tugas dari yang pertama. Jadi itu mungkin bekerja dengan tes penunjuk diikuti oleh pengujian huruf demi huruf Jadi, ya, saya penuh omong kosong :) terima kasih untuk mencerahkan saya ...
Juan Mendes
17

Jika Anda menggunakan jQuery, satu opsi yang bagus (terutama untuk situasi yang rumit) adalah menggunakan metode extended jQuery .

function foo(options) {

    default_options = {
        timeout : 1000,
        callback : function(){},
        some_number : 50,
        some_text : "hello world"
    };

    options = $.extend({}, default_options, options);
}

Jika Anda memanggil fungsi maka seperti ini:

foo({timeout : 500});

Variabel opsi akan menjadi:

{
    timeout : 500,
    callback : function(){},
    some_number : 50,
    some_text : "hello world"
};
Greg Tatum
sumber
15

Ini adalah salah satu dari sedikit kasus di mana saya menemukan tes:

if(! argument2) {  

}

bekerja dengan sangat baik dan membawa implikasi yang benar secara sintaksis.

(Dengan pembatasan simultan bahwa saya tidak akan mengizinkan nilai nol yang sah argument2yang memiliki arti lain; tapi itu akan sangat membingungkan.)

EDIT:

Ini adalah contoh yang sangat bagus dari perbedaan gaya antara bahasa yang diketik longgar dan diketik kuat; dan opsi gaya yang diberikan javascript dalam sekop.

Preferensi pribadi saya (tanpa kritik dimaksudkan untuk preferensi lain) adalah minimalis. Semakin sedikit kode yang harus dikatakan, selama saya konsisten dan ringkas, semakin sedikit orang lain yang harus memahami untuk menyimpulkan dengan benar makna saya.

Salah satu implikasi dari preferensi itu adalah bahwa saya tidak ingin - tidak merasa berguna untuk - menumpuk banyak tes tipe-dependensi. Sebagai gantinya, saya mencoba untuk membuat kode itu seperti apa artinya; dan hanya menguji untuk apa saya benar-benar perlu menguji.

Salah satu kejengkelan yang saya temukan dalam kode orang lain adalah perlu mencari tahu apakah mereka berharap, dalam konteks yang lebih besar, untuk benar-benar mengalami kasus-kasus yang mereka uji. Atau jika mereka mencoba menguji segala kemungkinan, dengan harapan mereka tidak cukup mengantisipasi konteksnya. Yang berarti saya akhirnya perlu melacak mereka secara mendalam di kedua arah sebelum saya dapat dengan yakin menolak atau memodifikasi apa pun. Saya pikir ada kemungkinan mereka melakukan berbagai tes karena mereka meramalkan keadaan di mana mereka akan diperlukan (dan yang biasanya tidak jelas bagi saya).

(Saya menganggap itu adalah kelemahan serius dalam cara orang-orang ini menggunakan bahasa yang dinamis. Terlalu sering orang tidak mau menyerahkan semua tes statis, dan akhirnya berpura-pura.)

Saya telah melihat ini paling mencolok dalam membandingkan kode ActionScript 3 yang komprehensif dengan kode javascript yang elegan. AS3 dapat 3 atau 4 kali lebih besar dari js, dan keandalan yang saya curigai setidaknya tidak lebih baik, hanya karena jumlah (3-4X) keputusan pengkodean yang dibuat.

Seperti yang Anda katakan, Shog9, YMMV. : D

dkretz
sumber
1
if (! argumen2) argument2 = 'default' setara dengan argument2 = argument2 || 'Default' - Saya menemukan versi kedua secara visual lebih menyenangkan ...
Christoph
5
Dan saya merasa lebih verbal dan mengganggu; tapi itu pilihan pribadi, saya yakin.
dkretz
4
@le dorfier: itu juga menghalangi penggunaan string kosong, 0, dan boolean false.
Shog9
@le dorfier: di luar estetika, ada satu perbedaan utama: yang terakhir secara efektif menciptakan jalur eksekusi kedua, yang mungkin menggoda pengelola yang ceroboh untuk menambahkan perilaku di luar penugasan sederhana dari nilai default. YMMV, tentu saja.
Shog9
3
bagaimana jika parameter2 adalah boolean === false; atau fungsi {return false;}?
fresko
7

Ada perbedaan yang signifikan. Mari kita siapkan beberapa test case:

var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");

Dengan metode pertama yang Anda jelaskan, hanya tes kedua yang akan menggunakan nilai default. Metode kedua akan default semua kecuali yang pertama (seperti JS akan mengkonversi undefined, null, 0, dan ""ke boolean false. Dan jika Anda menggunakan metode Tom, hanya tes keempat akan menggunakan default!

Metode mana yang Anda pilih benar-benar tergantung pada perilaku yang Anda maksudkan. Jika nilai selain undefineddiizinkan argument2, maka Anda mungkin ingin beberapa variasi pada yang pertama; jika nilai yang bukan nol, tidak nol, tidak kosong diinginkan, maka metode kedua adalah ideal - bahkan, sering digunakan untuk dengan cepat menghilangkan berbagai nilai dari pertimbangan.

Shog9
sumber
7
url = url === undefined ? location.href : url;
Lamy
sumber
Tulang telanjang menjawab. Beberapa penjelasan tidak akan menyakitkan.
Paul Rooney
Ini adalah operator ternary yang secara singkat mengatakan: Jika url tidak terdefinisi (hilang), tetapkan variabel url menjadi location.href (halaman web saat ini), jika tidak, tetapkan variabel url menjadi url yang ditentukan.
rmooney
5

Dalam ES6 (ES2015) Anda dapat menggunakan parameter Default

function Test(arg1 = 'Hello', arg2 = 'World!'){
  alert(arg1 + ' ' +arg2);
}

Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!

Andriy2
sumber
Jawaban ini sangat menarik dan bisa berguna untuk diop. Meskipun itu tidak benar-benar menjawab pertanyaan
Ulysse BN
Seperti yang saya lihat - dia ingin mendefinisikan arg jika tidak didefinisikan, jadi saya memposting beberapa informasi bermanfaat
Andriy2
Maksud saya adalah Anda tidak menjawab Cara terbaik untuk menentukan apakah argumen tidak dikirim ke fungsi JavaScript . Tapi pertanyaannya bisa dijawab dengan menggunakan argumen default: misalnya memberi nama Anda argumen dengan "default value"dan memeriksa apakah nilainya memang "default value".
Ulysse BN
4

Maaf, saya masih belum bisa berkomentar, jadi untuk menjawab jawaban Tom ... Dalam javascript (undefined! = Null) == false Pada kenyataannya fungsi itu tidak berfungsi dengan "null", Anda harus menggunakan "undefined"

Luca Matteis
sumber
1
Dan Tom mendapat dua upvotes untuk jawaban yang salah - selalu menyenangkan mengetahui seberapa baik sistem komunitas ini bekerja;)
Christoph
3

Mengapa tidak menggunakan !!operator? Operator ini, ditempatkan sebelum variabel, mengubahnya menjadi boolean (jika saya mengerti dengan baik), jadi !!undefineddan !!null(dan bahkan !!NaN, yang bisa sangat menarik) akan kembali false.

Inilah contohnya:

function foo(bar){
    console.log(!!bar);
}

foo("hey") //=> will log true

foo() //=> will log false
RallionRl
sumber
Tidak bekerja dengan string boolean true, zero dan empty. Misalnya, foo (0); akan login salah, tetapi foo (1) akan login benar
rosell.dk
2

Akan lebih mudah untuk mendekati deteksi argumen dengan membangkitkan fungsi Anda dengan Object of opsional properties:

function foo(options) {
    var config = { // defaults
        list: 'string value',
        of: [a, b, c],
        optional: {x: y},
        objects: function(param){
           // do stuff here
        }
    }; 
    if(options !== undefined){
        for (i in config) {
            if (config.hasOwnProperty(i)){
                if (options[i] !== undefined) { config[i] = options[i]; }
            }
        }
    }
}
1nfiniti
sumber
2

Ada cara yang sulit juga untuk menemukan, apakah suatu parameter diteruskan ke suatu fungsi atau tidak . Lihat contoh di bawah ini:

this.setCurrent = function(value) {
  this.current = value || 0;
};

Ini perlu berarti bahwa jika nilai valuetidak ada / lulus - tetapkan ke 0.

Cukup keren ya!

Mohammed Zameer
sumber
1
Ini sebenarnya berarti "jika valuesetara dengan false, set ke 0." Ini adalah perbedaan yang halus tetapi sangat penting.
Charles Wood
@CharlesWood Nilai tidak lulus / berarti hadir hanya palsu
Mohammed Zameer
1
Tentu, jika itu sesuai dengan kebutuhan untuk fungsi Anda. Tetapi jika, misalnya, parameter Anda adalah boolean, maka truedan falsemerupakan nilai yang valid, dan Anda mungkin ingin memiliki perilaku ketiga ketika parameter tidak dilewati sama sekali (terutama jika fungsi tersebut memiliki lebih dari satu parameter).
Charles Wood
1
Dan saya harus mengakui bahwa ini adalah argumen besar dalam ilmu komputer dan mungkin hanya berakhir dengan pendapat: D
Charles Wood
@CharlesWood maaf sudah terlambat ke pesta. Saya menyarankan Anda untuk menambahkan ini dalam jawaban itu sendiri dengan opsi edit
Mohammed Zameer
1

Beberapa kali Anda mungkin juga ingin memeriksa jenis, khususnya jika Anda menggunakan fungsi sebagai pengambil dan penyetel. Kode berikut adalah ES6 (tidak akan berjalan di EcmaScript 5 atau lebih lama):

class PrivateTest {
    constructor(aNumber) {
        let _aNumber = aNumber;

        //Privileged setter/getter with access to private _number:
        this.aNumber = function(value) {
            if (value !== undefined && (typeof value === typeof _aNumber)) {
                _aNumber = value;
            }
            else {
                return _aNumber;
            }
        }
    }
}
nbloqs
sumber
0
function example(arg) {
  var argumentID = '0'; //1,2,3,4...whatever
  if (argumentID in arguments === false) {
    console.log(`the argument with id ${argumentID} was not passed to the function`);
  }
}

Karena array mewarisi dari Object.prototype. Pertimbangkan ⇑ untuk membuat dunia lebih baik.

Dibesarkan
sumber
-1

fnCalledFunction (Param1, Param2, window.YourOptionalParameter)

Jika fungsi di atas dipanggil dari banyak tempat dan Anda yakin 2 parameter pertama dilewatkan dari setiap tempat tetapi tidak yakin tentang parameter 3 maka Anda dapat menggunakan jendela.

window.param3 akan menangani jika tidak ditentukan dari metode pemanggil.

Goutam Panda
sumber