Berbagai argumen vs. opsi objek

157

Saat membuat fungsi JavaScript dengan beberapa argumen, saya selalu dihadapkan dengan pilihan ini: melewati daftar argumen vs. melewati objek opsi.

Sebagai contoh saya menulis fungsi untuk memetakan nodeList ke array:

function map(nodeList, callback, thisObject, fromIndex, toIndex){
    ...
}

Saya malah bisa menggunakan ini:

function map(options){
    ...
}

di mana opsi adalah objek:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

Mana yang direkomendasikan? Apakah ada pedoman kapan menggunakan satu vs yang lain?

[Pembaruan] Tampaknya ada konsensus yang mendukung objek opsi, jadi saya ingin menambahkan komentar: salah satu alasan mengapa saya tergoda untuk menggunakan daftar argumen dalam kasus saya adalah untuk memiliki perilaku yang konsisten dengan JavaScript. dibangun dalam metode array.map.

Christophe
sumber
2
Opsi kedua memberi Anda argumen bernama, yang menurut saya bagus.
Werner Kvalem Vesterås
Apakah argumen itu opsional atau diperlukan?
Aku Benci Malas
@ user1689607 dalam contoh saya tiga terakhir adalah opsional.
Christophe
Karena dua argumen terakhir Anda sangat mirip, jika pengguna hanya melewati satu atau yang lain, Anda tidak akan pernah benar-benar tahu yang mana yang dimaksudkan. Karena itu, Anda hampir membutuhkan argumen bernama. Tapi saya bisa menghargai bahwa Anda ingin mempertahankan API yang mirip dengan API asli.
I Hate Lazy
1
Pemodelan setelah API asli bukanlah hal yang buruk, jika fungsi Anda melakukan sesuatu yang serupa. Itu semua bermuara pada "apa yang membuat kode paling readablae." Array.prototype.mapmemiliki API sederhana yang seharusnya tidak membingungkan pembuat kode yang setengah berpengalaman.
Jeremy J Starcher

Jawaban:

160

Seperti banyak yang lain, saya sering lebih suka melewatkan options objectfungsi bukan melewati daftar panjang parameter, tetapi itu benar-benar tergantung pada konteks yang tepat.

Saya menggunakan keterbacaan kode sebagai tes lakmus.

Misalnya, jika saya memiliki panggilan fungsi ini:

checkStringLength(inputStr, 10);

Saya pikir kode itu cukup mudah dibaca dan melewati parameter individual.

Di sisi lain, ada fungsi dengan panggilan seperti ini:

initiateTransferProtocol("http", false, 150, 90, null, true, 18);

Benar-benar tidak dapat dibaca kecuali jika Anda melakukan riset. Di sisi lain, kode ini dibaca dengan baik:

initiateTransferProtocol({
  "protocol": "http",
  "sync":      false,
  "delayBetweenRetries": 150,
  "randomVarianceBetweenRetries": 90,
  "retryCallback": null,
  "log": true,
  "maxRetries": 18
 });

Ini lebih merupakan seni daripada sains, tetapi jika saya harus menyebutkan aturan praktis:

Gunakan parameter opsi jika:

  • Anda memiliki lebih dari empat parameter
  • Salah satu parameter bersifat opsional
  • Anda harus mencari fungsi untuk mencari tahu parameter apa yang diperlukan
  • Jika seseorang mencoba mencekikmu sambil berteriak, "ARRRRRG!"
Jeremy J Starcher
sumber
10
Jawaban yang bagus Tergantung. Waspadai perangkap boolean ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html
Trevor Dixon
2
Oh ya ... saya lupa tentang tautan itu. Itu benar-benar membuat saya memikirkan kembali bagaimana API bekerja dan saya bahkan menulis ulang beberapa potong kode setelah mengetahui saya melakukan hal-hal bodoh. Terima kasih!
Jeremy J Starcher
1
'Anda pernah harus mencari fungsi untuk mencari tahu parameter apa yang diperlukan' - Menggunakan objek opsi sebagai gantinya, tidakkah Anda harus selalu mencari metode untuk mencari tahu bahwa kunci diperlukan dan apa namanya. Intellisense di IDE tidak memunculkan informasi ini, sementara params melakukannya. Di sebagian besar IDE, Anda dapat mengarahkan mouse ke atas metode dan itu akan menunjukkan kepada Anda apa params.
simbolo
1
Jika Anda tertarik pada konsekuensi kinerja dari sedikit 'pengkodean praktik terbaik' ini, inilah jsPerf yang menguji kedua cara: jsperf.com/function-boolean-arguments-vs-options-object/10 Perhatikan sedikit perubahan dalam alternatif ketiga, di mana saya menggunakan objek opsi 'preset' (konstan), yang dapat dilakukan ketika Anda memiliki banyak panggilan (selama masa pakai runtime Anda, misalnya halaman web Anda) dengan pengaturan yang sama, dikenal pada waktu pengembangan (singkatnya : ketika nilai opsi Anda agak dikodekan dalam kode sumber Anda).
Ger Hobbelt
2
@Sean Jujur, saya tidak lagi menggunakan gaya pengkodean ini sama sekali. Saya telah beralih ke TypeScript dan menggunakan parameter bernama.
Jeremy J Starcher
28

Beragam argumen sebagian besar untuk parameter wajib. Tidak ada yang salah dengan mereka.

Jika Anda memiliki parameter opsional, itu menjadi rumit. Jika salah satu dari mereka bergantung pada yang lain, sehingga mereka memiliki urutan tertentu (misalnya yang keempat membutuhkan yang ketiga), Anda masih harus menggunakan beberapa argumen. Hampir semua metode EcmaScript dan DOM asli bekerja seperti ini. Contoh yang baik adalah openmetode XMLHTTPrequests , di mana 3 argumen terakhir adalah opsional - aturannya seperti "tidak ada kata sandi tanpa pengguna" (lihat juga MDN docs ).

Objek opsi berguna dalam dua kasus:

  • Anda memiliki begitu banyak parameter sehingga membingungkan: "Penamaan" akan membantu Anda, Anda tidak perlu khawatir tentang urutannya (terutama jika mereka dapat berubah)
  • Anda punya parameter opsional. Benda-benda sangat fleksibel, dan tanpa memesan Anda hanya melewati hal-hal yang Anda butuhkan dan tidak ada yang lain undefined.

Dalam kasus Anda, saya akan merekomendasikan map(nodeList, callback, options). nodelistdan callbackdiperlukan, tiga argumen lainnya hanya datang sesekali dan memiliki standar yang wajar.

Contoh lain adalah JSON.stringify. Anda mungkin ingin menggunakan spaceparameter tanpa melewati suatu replacerfungsi - maka Anda harus menelepon …, null, 4). Objek argumen mungkin lebih baik, meskipun tidak cukup masuk akal untuk hanya 2 parameter.

Bergi
sumber
+1 pertanyaan yang sama dengan @ trevor-dixon: apakah Anda pernah melihat campuran ini digunakan dalam praktik, misalnya di js libraries?
Christophe
Contohnya bisa menjadi metode ajax jQuery . Mereka menerima URL [wajib] sebagai argumen pertama, dan argumen opsi besar sebagai argumen kedua.
Bergi
sangat aneh! Saya tidak pernah memperhatikan ini sebelumnya. Saya selalu melihatnya menggunakan url sebagai properti pilihan ...
Christophe
Ya, jQuery melakukan hal yang aneh dengan parameter opsionalnya sambil tetap kompatibel :-)
Bergi
1
Menurut pendapat saya, ini adalah satu-satunya jawaban yang waras di sini.
Benjamin Gruenbaum
11

Menggunakan pendekatan 'opsi sebagai objek' akan menjadi yang terbaik. Anda tidak perlu khawatir tentang urutan properti dan ada lebih banyak fleksibilitas dalam data apa yang akan diteruskan (misalnya parameter opsional)

Membuat objek juga berarti opsi dapat dengan mudah digunakan pada banyak fungsi:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

function1(options){
    alert(options.nodeList);
}

function2(options){
    alert(options.fromIndex);
}
Fluidbyte
sumber
Asumsi (masuk akal) di sini adalah bahwa objek akan selalu memiliki pasangan kunci yang sama. Jika Anda bekerja dengan API omong kosong / tidak konsisten maka Anda memiliki masalah yang berbeda di tangan Anda.
backdesk
9

Saya pikir jika Anda membuat instance sesuatu atau memanggil metode objek, Anda ingin menggunakan objek opsi. Jika itu adalah fungsi yang beroperasi hanya pada satu atau dua parameter dan mengembalikan nilai, daftar argumen lebih disukai.

Dalam beberapa kasus, ada baiknya menggunakan keduanya. Jika fungsi Anda memiliki satu atau dua parameter yang diperlukan dan banyak parameter opsional, buat dua parameter pertama yang diperlukan dan yang ketiga sebagai hash opsi opsional.

Dalam contoh Anda, saya akan lakukan map(nodeList, callback, options). Nodelist dan callback diperlukan, cukup mudah untuk mengetahui apa yang terjadi hanya dengan membaca panggilan untuk itu, dan itu seperti fungsi peta yang ada. Opsi lain dapat dilewatkan sebagai parameter ketiga opsional.

Trevor Dixon
sumber
+1 menarik. Pernahkah Anda melihatnya digunakan dalam praktik, misalnya di perpustakaan js?
Christophe
7

Komentar Anda pada pertanyaan:

dalam contoh saya tiga terakhir adalah opsional.

Jadi mengapa tidak melakukan ini? (Catatan: Ini adalah Javascript yang cukup mentah. Biasanya saya akan menggunakan defaulthash dan memperbaruinya dengan opsi yang diteruskan dengan menggunakan Object.extend atau JQuery.extend atau serupa ..)

function map(nodeList, callback, options) {
   options = options || {};
   var thisObject = options.thisObject || {};
   var fromIndex = options.fromIndex || 0;
   var toIndex = options.toIndex || 0;
}

Jadi, sekarang karena sekarang jauh lebih jelas apa yang opsional dan apa yang tidak, semua ini adalah kegunaan fungsi yang valid:

map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
   thisObject: {some: 'object'},
});
map(nodeList, callback, {
   toIndex: 100,
});
map(nodeList, callback, {
   thisObject: {some: 'object'},
   fromIndex: 0,
   toIndex: 100,
});
Izkata
sumber
Ini mirip dengan jawaban @ trevor-dixon.
Christophe
5

Saya mungkin agak terlambat ke pesta dengan tanggapan ini, tetapi saya sedang mencari pendapat pengembang lain tentang topik ini dan menemukan utas ini.

Saya sangat tidak setuju dengan sebagian besar responden, dan berpihak pada pendekatan 'berbagai argumen'. Argumen utama saya adalah bahwa hal itu mencegah anti-pola lain seperti "bermutasi dan mengembalikan objek param", atau "meneruskan objek param yang sama ke fungsi lain". Saya telah bekerja di basis kode yang telah banyak menyalahgunakan pola-anti ini, dan kode debug yang melakukan ini dengan cepat menjadi tidak mungkin. Saya pikir ini adalah aturan praktis yang sangat spesifik untuk Javascript, karena Javascript tidak diketik dengan kuat dan memungkinkan objek terstruktur secara sewenang-wenang.

Pendapat pribadi saya adalah bahwa pengembang harus eksplisit ketika memanggil fungsi, menghindari memberikan data yang berlebihan dan menghindari modifikasi dengan referensi. Bukannya pola ini menghalangi penulisan kode yang singkat dan benar. Saya hanya merasa itu membuat proyek Anda jauh lebih mudah untuk jatuh ke dalam praktik pembangunan yang buruk.

Pertimbangkan kode mengerikan berikut:

function main() {
    const x = foo({
        param1: "something",
        param2: "something else",
        param3: "more variables"
    });

    return x;
}

function foo(params) {
    params.param1 = "Something new";
    bar(params);
    return params;
}


function bar(params) {
    params.param2 = "Something else entirely";
    const y = baz(params);
    return params.param2;
}

function baz(params) {
    params.params3 = "Changed my mind";
    return params;
}

Tidak hanya jenis ini memerlukan dokumentasi yang lebih eksplisit untuk menentukan maksud, tetapi juga memberikan ruang untuk kesalahan yang tidak jelas. Bagaimana jika pengembang memodifikasi param1di bar()? Menurut Anda, berapa lama waktu yang dibutuhkan untuk melihat melalui basis kode ukuran cukup untuk menangkap ini? Diakui, ini adalah contoh yang sedikit tidak jujur ​​karena mengasumsikan pengembang telah melakukan beberapa anti-pola pada titik ini. Tapi itu menunjukkan bagaimana objek yang lewat berisi parameter memungkinkan ruang yang lebih besar untuk kesalahan dan ambiguitas, membutuhkan tingkat kesadaran yang lebih besar dan ketaatan pada kebenaran konst.

Hanya dua sen saya tentang masalah ini!

ajxs
sumber
3

Tergantung.

Berdasarkan pengamatan saya pada desain perpustakaan populer itu, berikut adalah skenario yang harus kita gunakan objek pilihan:

  • Daftar parameter panjang (> 4).
  • Beberapa atau semua parameter bersifat opsional dan tidak bergantung pada urutan tertentu.
  • Daftar parameter mungkin tumbuh di pembaruan API di masa mendatang.
  • API akan dipanggil dari kode lain dan nama API tidak cukup jelas untuk memberi tahu makna parameter. Jadi mungkin perlu nama parameter yang kuat agar mudah dibaca.

Dan skenario untuk menggunakan daftar parameter:

  • Daftar parameter pendek (<= 4).
  • Sebagian besar atau semua parameter diperlukan.
  • Parameter opsional dalam urutan tertentu. (mis .: $ .get)
  • Mudah mengetahui arti parameter berdasarkan nama API.
Lynn Ning
sumber
2

Objek lebih disukai, karena jika Anda melewati objek, mudah untuk memperluas jumlah properti di objek itu dan Anda tidak harus melihat urutan argumen yang telah Anda berikan.

happyCoda
sumber
1

Untuk fungsi yang biasanya menggunakan beberapa argumen yang telah ditentukan, Anda sebaiknya menggunakan objek opsi. Contoh sebaliknya akan berupa fungsi yang mendapatkan argumen dengan jumlah tak terhingga seperti: setCSS ({height: 100}, {width: 200}, {background: "# 000"}).

Ilya Sidorovich
sumber
0

Saya akan melihat proyek javascript besar.

Hal-hal seperti google map Anda akan sering melihat bahwa objek yang dipakai membutuhkan objek tetapi fungsi memerlukan parameter. Saya akan berpikir ini ada hubungannya dengan argumen OPSI.

Jika Anda memerlukan argumen default atau argumen opsional, sebuah objek mungkin akan lebih baik karena lebih fleksibel. Tetapi jika Anda tidak menggunakan argumen fungsional yang normal, ini lebih eksplisit.

Javascript juga memiliki argumentsobjek. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

dm03514
sumber