Kesalahan metode rantai array pada nilai non-array yang tidak seperti metode kategori array lainnya.
_().pop(); // error
_('').pop(); // error
_().first() // undefined
Ini memunculkan masalah lain, haruskah kita menggunakan array-like ke array?
_('hello').first() // 'h'
_('hello').pop() // ?
Ini sepertinya masih menjadi masalah - kami menemukannya setelah menabrak versi kami dari 1.8.3 t0 1.9.0
Sunting: Sepertinya ini telah diperbaiki di 1.9.1, jika demikian, apakah masalah ini harus tetap terbuka?
Perilaku ini masih ada di 1.10.2.
Kontras yang sedikit lebih meyakinkan dalam opsi saya, karena keduanya melibatkan modifikasi nilai di tempat:
Object.assign(undefined, {a: 1}) // error
_().extend({a: 1}) // undefined
Array.prototype.push.call(undefined, 1) // error
_().push(1) // error
Saya agak setuju ini tidak konsisten.
Perlu dipertimbangkan dalam situasi dunia nyata seperti apa ini kemungkinan besar akan ditemui dan apa yang diharapkan dari objek yang dibungkus dalam kasus seperti itu.
Di tengah rantai, objek yang dibungkus dapat diprediksi. Misalnya, dalam rantai di bawah ini, objek yang dibungkus yang diteruskan ke push
akan berupa Array
atau undefined
. Untuk alasan ini, saya berpendapat bahwa metode pembungkus Array
harus menerapkan pemeriksaan nol. Ini mudah.
_.chain(something).map(f).push(x) // hoping to push x to an array
Jika objek terbungkus yang diprediksi lebih cenderung menjadi sesuatu selain array, maka itu bisa dibilang kesalahan programmer. Saya tidak keberatan melemparkan pengecualian dalam kasus itu.
_.chain(something).map(f).join('').push(x) // hoping to push x to a string??
Pada awal rantai, ceritanya mungkin tampak sedikit berbeda pada pandangan pertama, tetapi saya akan berpendapat bahwa itu sama dan bahwa kita harus membiarkannya di cek nol. Setidaknya harus terjadi bahwa programmer memiliki beberapa alasan untuk mengharapkan something
menjadi seperti array yang bisa berubah, karena jika tidak, tidak akan ada alasan untuk memanggil push
:
function giveMeAMutableArraylike(something) {
_.chain(something).push(x)
}
Harapan bahwa something
adalah seperti array yang dapat berubah dapat tidak terpenuhi karena berbagai alasan:
giveMeAMutableArraylike
akhirnya dipanggil dengan null
atau undefined
untuk alasan apa pun. Ini sebanding dengan contoh chain-in-the-middle pertama dan dapat diatasi dengan pemeriksaan nol yang sama.giveMeAMutableArraylike
melakukan sesuatu yang jelas-jelas tidak dapat dilakukan, yaitu melanggar kontrak. Ini sebanding dengan contoh rantai di tengah kedua. Sekali lagi, saya pikir tidak apa-apa untuk membuat kesalahan dalam kasus ini.something
adalah nilai kosong alih-alih array dengan elemen tunggal, yaitu v
alih-alih [v]
. Ini adalah situasi umum di banyak API JavaScript. Jika kontrak giveMeAMutableArraylike
memungkinkan hal ini, jelas salah untuk membuat kesalahan terlepas dari apa yang terjadi v
.Dalam kasus terakhir, Garis Bawah tidak dapat mengetahui apakah giveMeAMutableArraylike
mengizinkan elemen telanjang tunggal atau tidak, jadi terserah kepada pemrogram giveMeAMutableArraylike
untuk mengimplementasikan kontrak khususnya. Tidak ada perpustakaan seperti Underscore yang dapat melakukan hal yang benar untuk semua kemungkinan kontrak:
| Perilaku saat ini | Bungkus dalam array elemen tunggal
---|---|---
Kontrak memungkinkan elemen tunggal kosong | _Programmer perlu campur tangan_ | Garis bawah melakukan hal yang benar secara otomatis
Kontrak tidak mengizinkan elemen tunggal kosong | Garis bawah melakukan hal yang benar secara otomatis | _Programmer perlu campur tangan_
Juga, bagaimana Anda memutuskan apakah suatu nilai harus dibungkus? Beberapa kontrak mungkin ingin membungkus apa pun yang bukan Array
, sementara yang lain mungkin ingin meneruskan arguments
dan objek biasa apa adanya. Sekali lagi, ini adalah sesuatu yang tidak dapat ditebak oleh perpustakaan seperti Underscore atas nama pengguna.
Situasi yang lebih eksotis juga dapat dibayangkan, misalnya kontrak yang mengizinkan objek seperti array yang tidak dapat diubah seperti string dan yang akan mengubahnya menjadi Array
terlebih dahulu. Tidak mungkin untuk mencakup semua variasi yang mungkin. Dengan prinsip perubahan terkecil, tidak ada argumen yang meyakinkan untuk mendukung beberapa kontrak dengan mengorbankan kontrak yang sudah didukung. Ada juga alasan kuat untuk menjaga implementasi seminimal mungkin.
Jadi saya pikir solusi yang tepat adalah dengan memasukkan cek nol saja dan berhenti di situ. Ini adalah satu-satunya perubahan pada perilaku Garis Bawah yang tampaknya dapat dipertahankan dalam semua situasi yang mungkin dan juga konsisten dengan perilaku _.extend
. Pemeriksaan nol masih bisa dianggap sebagai perbaikan bug, saat melakukan lebih dari itu akan dengan cepat mengubahnya menjadi perubahan yang melanggar sewenang-wenang.
@jashkenas bisakah Anda menyinari ini? Jika Anda setuju, saya akan menyiapkan permintaan tarik yang mengimplementasikan cek nol.
@jgonggrijp — Bisakah Anda menguraikan satu atau dua kalimat dengan perilaku yang ingin Anda terapkan dengan cek nol?
Apakah metode array akan secara eksplisit mengeluarkan pengecualian ketika dipanggil pada nilai nol? Atau mereka akan noop ketika dipanggil dengan nilai nullish?
@jashkenas Ya. Perilaku yang akan saya terapkan adalah no-op, mengikuti gaya fungsi array Garis Bawah:
Kecuali bahwa cek length
tidak diperlukan, jadi saya hanya akan memasukkan
if (obj == null) return chainresult(this, obj);
antara dua garis ini:
Tes plus tentu saja, dan jika tes itu menunjukkan kebutuhan, mungkin juga pemeriksaan nol yang mengubah metode menjadi no-op sebelum baris ini:
Kedengarannya bagus untuk saya!
Komentar yang paling membantu
Ini memunculkan masalah lain, haruskah kita menggunakan array-like ke array?