Ada banyak hal sederhana yang dapat Anda lakukan untuk meningkatkan JavaScript. Berikut adalah beberapa metode yang kami gunakan untuk menulis JS yang lebih baik
Ryland Goldstein Kepala Produk di Temporal
Dalam Survei Dev 2019 kami, kami menanyakan jenis konten apa yang ingin dilihat pengguna Stack Overflow di luar pertanyaan dan jawaban. Yang paling banyak adalah “artikel teknologi yang ditulis oleh pengembang lain. “Jadi mulai sekarang kami akan rutin menerbitkan artikel dari para kontributor. Jika Anda memiliki ide dan ingin mengirimkan penawaran, Anda dapat mengirim email ke pitches@stackoverflow. com
Halo, saya Ryland Goldstein, seorang staf produk yang mengerjakan Reshuffle di Binaris. Ini adalah bagian kedua saya untuk Stack Overflow. Mari kita menggali lebih dalam
Saya tidak melihat cukup banyak orang berbicara tentang cara praktis untuk meningkatkan JavaScript. Berikut adalah beberapa metode teratas yang saya gunakan untuk menulis JS yang lebih baik
Gunakan TypeScript
Hal nomor satu yang dapat Anda lakukan untuk meningkatkan JS Anda adalah dengan tidak menulis JS. Untuk yang belum tahu, TypeScript (TS) adalah superset JS yang "dikompilasi" (apa pun yang berjalan di JS berjalan di TS). TS menambahkan sistem pengetikan opsional yang komprehensif di atas pengalaman vanilla JS. Untuk waktu yang lama, dukungan TS di seluruh ekosistem cukup tidak konsisten sehingga saya merasa tidak nyaman untuk merekomendasikannya. Untungnya, hari-hari itu sudah lama berlalu dan sebagian besar kerangka mendukung TS di luar kotak. Sekarang kita semua berada di halaman yang sama tentang apa itu TS, mari kita bicara tentang mengapa Anda ingin menggunakannya
TypeScript memberlakukan keamanan tipe
Keamanan tipe menjelaskan proses di mana kompiler memverifikasi bahwa semua tipe digunakan dengan cara yang legal di seluruh bagian kode. Dengan kata lain, jika Anda membuat fungsi console.log(foo("two")); // invalid TS code8 yang menggunakan angka
function foo(someNum: number): number { return someNum + 5; }_Fungsi console.log(foo("two")); // invalid TS code_8 itu hanya boleh dipanggil dengan nomor
Selain biaya tambahan untuk menambahkan tipe ke kode Anda, tidak ada kerugian dari penerapan keamanan tipe. Manfaatnya di sisi lain, terlalu besar untuk diabaikan. Keamanan jenis memberikan tingkat perlindungan ekstra terhadap kesalahan/bug umum, yang merupakan berkah bagi bahasa tanpa hukum seperti JS
Jenis TypeScript memungkinkan pemfaktoran ulang aplikasi yang lebih besar
Memfaktorkan ulang aplikasi JS yang besar bisa menjadi mimpi buruk yang nyata. Sebagian besar rasa sakit dari refactoring JS adalah karena fakta bahwa itu tidak menerapkan tanda tangan fungsi. Ini berarti fungsi JS tidak pernah bisa disalahgunakan. Misalnya, jika saya memiliki fungsi function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }_0 yang digunakan oleh 1000 layanan berbeda
function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }dan saya sedikit mengubah tanda panggilan
Saya harus 100% yakin, bahwa setiap tempat di mana fungsi ini digunakan (ribuan tempat), saya memperbarui penggunaannya dengan benar. Jika saya melewatkan satu saja, kredensial saya bisa bocor. Berikut skenario yang sama dengan TS
beforefunction myAPITS(someNum: number, someString: string) { .. }aftergood0Seperti yang Anda lihat, fungsi function myAPI(someNum, someString) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}1 mengalami perubahan yang sama dengan JavaScript. Namun alih-alih menghasilkan JavaScript yang valid, kode ini menghasilkan TypeScript yang tidak valid, karena ribuan tempat yang digunakan sekarang menyediakan jenis yang salah. Dan karena keamanan jenis yang telah kita bahas sebelumnya, ribuan kasus tersebut akan memblokir kompilasi dan kredensial Anda tidak akan bocor (itu selalu menyenangkan)
TypeScript membuat komunikasi arsitektur tim lebih mudah
Ketika TS diatur dengan benar, akan sulit untuk menulis kode tanpa terlebih dahulu menentukan antarmuka dan kelas Anda. Ini menyediakan cara untuk berbagi proposal arsitektur yang ringkas dan komunikatif. Sebelum TS, ada solusi lain untuk masalah ini, tetapi tidak ada yang menyelesaikannya secara asli tanpa membuat Anda melakukan pekerjaan ekstra. Misalnya, jika saya ingin mengusulkan tipe function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }2 baru untuk backend saya, saya dapat mengirimkan yang berikut ini ke rekan satu tim menggunakan TS
good1Saya sudah harus menulis kodenya, tetapi sekarang saya dapat membagikan kemajuan tambahan saya dan mendapatkan umpan balik tanpa menginvestasikan lebih banyak waktu. Saya tidak tahu apakah TS secara inheren kurang rentan bug dibandingkan JS. Saya sangat percaya bahwa memaksa pengembang untuk mendefinisikan antarmuka dan API menghasilkan kode yang lebih baik terlebih dahulu
Secara keseluruhan, TS telah berkembang menjadi alternatif yang matang dan lebih dapat diprediksi dari vanilla JS. Pengembang pasti masih harus merasa nyaman dengan vanilla JS, tetapi sebagian besar proyek baru yang saya mulai hari ini adalah TS sejak awal
Gunakan Fitur Modern
JavaScript adalah salah satu bahasa pemrograman paling populer (jika bukan yang paling banyak) di dunia. Anda mungkin berharap bahwa bahasa berusia 20+ tahun yang digunakan oleh ratusan juta orang sebagian besar sudah diketahui sekarang, tetapi yang terjadi justru sebaliknya. Baru-baru ini, banyak perubahan dan penambahan telah dilakukan pada JS (ya saya tahu, secara teknis ECMAScript), yang secara mendasar mengubah pengalaman pengembang. Sebagai seseorang yang baru mulai menulis JS dalam dua tahun terakhir, saya mendapat keuntungan karena masuk tanpa bias atau ekspektasi. Ini menghasilkan pilihan yang jauh lebih pragmatis tentang fitur bahasa apa yang akan digunakan dan mana yang harus dihindari
function myAPI(someNum, someString) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}3 dan function myAPI(someNum, someString) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}4
Untuk waktu yang lama, callback asinkron dan digerakkan oleh peristiwa adalah bagian yang tidak dapat dihindari dari pengembangan JS
good2good3Saya tidak akan menghabiskan waktu untuk menjelaskan mengapa hal di atas bermasalah (tetapi saya pernah melakukannya sebelumnya). Untuk mengatasi masalah callback, konsep baru—Janji—ditambahkan ke JS. Janji memungkinkan Anda untuk menulis logika asinkron sambil menghindari masalah bersarang yang sebelumnya mengganggu kode berbasis panggilan balik
good4good5Keuntungan terbesar Janji dibandingkan panggilan balik adalah keterbacaan dan keteraturan rantai
Sementara Janji itu bagus, mereka masih meninggalkan sesuatu yang diinginkan. Bagi banyak orang, pengalaman Janji masih terlalu mirip dengan panggilan balik. Secara khusus, pengembang meminta alternatif untuk model Janji. Untuk mengatasinya, komite ECMAScript memutuskan untuk menambahkan metode baru dalam memanfaatkan promise, function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3 dan function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4
good6good7Satu-satunya peringatan adalah, apa pun yang Anda function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4 harus telah dinyatakan function myAPI(someNum, someString) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3
good8good9Juga dimungkinkan untuk function myAPI(someNum, someString) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_4 Promise secara langsung karena fungsi function myAPI(someNum, someString) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}3 sebenarnya hanyalah pembungkus Promise yang mewah. Ini juga berarti kode function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_1 dan kode Promise secara fungsional setara. Jadi jangan ragu untuk menggunakan function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_1 tanpa merasa bersalah
function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}3 dan function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}4
Untuk sebagian besar keberadaan JS, hanya ada satu kualifikasi cakupan variabel. function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }_5. function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }5 memiliki beberapa aturan yang cukup unik/menarik terkait dengan cara menangani ruang lingkup. Perilaku pelingkupan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }_5 tidak konsisten dan membingungkan dan telah menghasilkan perilaku yang tidak terduga dan karenanya bug sepanjang masa pakai JS. Tetapi pada ES6, ada alternatif untuk function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }5. function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4 dan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3. Praktis tidak perlu lagi menggunakan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }5, jadi jangan. Logika apa pun yang menggunakan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }5 selalu dapat dikonversi menjadi kode berbasis function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4 dan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3 yang setara
Adapun kapan harus menggunakan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4 vs function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3, saya selalu memulai dengan mendeklarasikan semuanya function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4. function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }4 jauh lebih membatasi dan "tidak dapat diubah", yang biasanya menghasilkan kode yang lebih baik. Tidak ada satu ton "skenario nyata" di mana menggunakan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3 diperlukan, saya akan mengatakan 1/20 variabel saya nyatakan dengan function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }3. Sisanya semua function myAPI(someString, someNum) { if (someNum > 0) { leakCredentials(); } else { console.log(someString); } }_4
Saya mengatakan function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_4 adalah "tidak dapat diubah" karena tidak bekerja dengan cara yang sama seperti function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}4 di C/C++. Apa function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_4 berarti waktu proses JavaScript adalah bahwa referensi ke variabel function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}4 itu tidak akan pernah berubah. Ini tidak berarti konten yang disimpan pada referensi tersebut tidak akan pernah berubah. Untuk tipe primitif (angka, boolean, dll. ), function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}_4 diterjemahkan menjadi kekekalan (karena ini adalah satu alamat memori). Tapi untuk semua objek (kelas, array, dicts), function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}4 tidak menjamin kekekalan
Panah function myAPITS(someNum: number, someString: string) { .. }_8 Fungsi
Fungsi panah adalah metode ringkas untuk mendeklarasikan fungsi anonim di JS. Fungsi anonim mendeskripsikan fungsi yang tidak diberi nama secara eksplisit. Biasanya, fungsi anonim diteruskan sebagai callback atau event hook
console.log(foo(2)); // prints "7"0console.log(foo(2)); // prints "7"1Sebagian besar, tidak ada yang "salah" dengan gaya ini. Fungsi anonim vanilla berperilaku "menarik" dalam hal ruang lingkup, yang dapat/telah mengakibatkan banyak bug yang tidak terduga. Kami tidak perlu khawatir tentang itu lagi berkat fungsi panah. Ini adalah kode yang sama yang diimplementasikan dengan fungsi panah
Selain jauh lebih ringkas, fungsi panah memiliki perilaku pelingkupan yang jauh lebih praktis. Fungsi panah mewarisi function myAPITS(someNum: number, someString: string) { .. }_9 dari cakupan tempat mereka didefinisikan
Dalam beberapa kasus, fungsi panah bisa lebih ringkas
console.log(foo(2)); // prints "7"_4Fungsi panah yang berada pada satu baris menyertakan pernyataan after0 implisit. Tidak perlu tanda kurung atau titik koma dengan fungsi panah satu baris
Saya ingin menjelaskannya. Ini bukan situasi function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}5; . Yang sedang berkata, saya telah menemukan bahwa jika Anda selalu default ke fungsi panah, Anda akhirnya melakukan lebih sedikit debugging dibandingkan dengan default ke fungsi anonim vanilla
Menyebarkan Operator after2
Mengekstrak pasangan kunci/nilai dari satu objek dan menambahkannya sebagai turunan dari objek lain adalah skenario yang sangat umum. Secara historis, ada beberapa cara untuk melakukannya, tetapi semua metode itu cukup kikuk
console.log(foo(2)); // prints "7"_6Pola ini sangat umum, sehingga pendekatan di atas dengan cepat menjadi membosankan. Berkat operator spread, tidak perlu menggunakannya lagi
console.log(foo(2)); // prints "7"_7Bagian terbaiknya adalah, ini juga berfungsi mulus dengan array
console.log(foo(2)); // prints "7"_8Ini mungkin bukan fitur JS terbaru yang paling penting, tapi ini salah satu favorit saya
Templat Literal (String Templat)
String adalah salah satu konstruksi pemrograman yang paling umum. Inilah mengapa sangat memalukan bahwa mendeklarasikan string secara native masih kurang didukung dalam banyak bahasa. Untuk waktu yang lama, JS termasuk dalam keluarga "string jelek". Namun penambahan literal template menempatkan JS dalam kategori tersendiri. Template literal secara native dan mudah memecahkan dua masalah terbesar dengan menulis string. menambahkan konten dinamis dan menulis string yang menjembatani banyak baris
console.log(foo(2)); // prints "7"_9Saya pikir kode berbicara untuk dirinya sendiri. Implementasi yang luar biasa
Penghancuran Objek
Penghancuran objek adalah cara untuk mengekstraksi nilai dari kumpulan data (objek, larik, dll. ), tanpa harus mengulangi data atau mengakses kuncinya secara eksplisit
perusakan
Tapi tunggu, masih ada lagi. Anda juga dapat menentukan destrukturisasi dalam tanda tangan suatu fungsi
Ini juga bekerja dengan array
no good5no good6Ada banyak fitur modern lainnya yang harus Anda manfaatkan. Berikut adalah beberapa orang lain yang menonjol bagi saya
- Parameter Istirahat
- Impor Lebih dari Kebutuhan
- Temukan Elemen Array
Selalu Asumsikan Sistem Anda Terdistribusi
Saat menulis aplikasi paralel, tujuan Anda adalah mengoptimalkan jumlah pekerjaan yang Anda lakukan pada satu waktu. Jika Anda memiliki empat inti yang tersedia dan kode Anda hanya dapat menggunakan satu inti, 75% dari potensi Anda terbuang sia-sia. Ini berarti pemblokiran, operasi sinkron adalah musuh utama komputasi paralel. Tetapi mengingat JS adalah bahasa utas tunggal, banyak hal tidak berjalan pada banyak inti. Jadi apa gunanya?
JS adalah utas tunggal, tetapi bukan file tunggal (seperti baris di sekolah). Meskipun tidak paralel, itu masih bersamaan. Mengirim permintaan HTTP mungkin memakan waktu beberapa detik atau bahkan beberapa menit, jadi jika JS berhenti mengeksekusi kode hingga respons kembali dari permintaan tersebut, bahasa tersebut tidak akan dapat digunakan
JavaScript menyelesaikan ini dengan loop acara. Loop peristiwa mengulang melalui peristiwa terdaftar dan mengeksekusinya berdasarkan logika penjadwalan/prioritas internal. Inilah yang memungkinkan pengiriman ribuan permintaan HTTP secara bersamaan atau membaca banyak file dari disk secara bersamaan. Inilah tangkapannya. JavaScript hanya dapat menggunakan kemampuan ini jika Anda menggunakan fitur yang tepat. Contoh paling sederhana adalah for-loop
Vanilla for-loop adalah salah satu konstruksi paling tidak paralel yang ada dalam pemrograman. Pada pekerjaan terakhir saya, saya memimpin sebuah tim yang menghabiskan waktu berbulan-bulan mencoba mengubah after3 lang for-loop tradisional menjadi kode paralel otomatis. Ini pada dasarnya adalah masalah yang tidak mungkin, hanya dapat dipecahkan dengan menunggu pembelajaran mendalam untuk meningkat. Kesulitan memparalelkan for-loop berasal dari beberapa pola yang bermasalah. For-loop berurutan sangat jarang, tetapi mereka sendiri tidak memungkinkan untuk menjamin dekomposisi for-loop
no good_8Kode ini hanya menghasilkan hasil yang diinginkan jika dijalankan secara berurutan, iterasi demi iterasi. Jika Anda mencoba menjalankan beberapa iterasi sekaligus, prosesor mungkin salah bercabang berdasarkan nilai yang tidak akurat, yang membuat hasilnya tidak valid. Kami akan melakukan percakapan yang berbeda jika ini adalah kode C, karena penggunaannya berbeda dan ada beberapa trik yang dapat dilakukan kompiler dengan loop. Dalam JavaScript, for-loop tradisional hanya boleh digunakan jika benar-benar diperlukan. Jika tidak, gunakan konstruksi berikut
peta
peta dengan indeks
console.log(foo("two")); // invalid TS code0untuk setiap
console.log(foo("two")); // invalid TS code_1Saya akan menjelaskan mengapa ini merupakan peningkatan dari for-loop tradisional. Alih-alih menjalankan setiap iterasi secara berurutan (berurutan), konstruksi seperti after4 mengambil semua elemen dan mengirimkannya sebagai peristiwa individual ke fungsi peta yang ditentukan pengguna. Sebagian besar, iterasi individu tidak memiliki koneksi atau ketergantungan yang melekat satu sama lain, yang memungkinkan mereka untuk berjalan secara bersamaan. Ini bukan untuk mengatakan bahwa Anda tidak dapat mencapai hal yang sama dengan for-loop. Bahkan, itu akan terlihat seperti ini
console.log(foo("two")); // invalid TS code_2Seperti yang Anda lihat, for-loop tidak mencegah saya melakukannya dengan cara yang benar, tetapi tentu saja juga tidak membuatnya lebih mudah. Bandingkan dengan versi peta
console.log(foo("two")); // invalid TS code_3Seperti yang Anda lihat, after_4 baru saja berfungsi. Keuntungan peta menjadi lebih jelas jika Anda ingin memblokir hingga semua operasi asinkron individual selesai. Dengan kode for-loop, Anda perlu mengatur sendiri sebuah array. Inilah versi after_4
console.log(foo("two")); // invalid TS code4console.log(foo("two")); // invalid TS code5Ada banyak kasus di mana for-loop akan sama berkinerjanya (atau mungkin lebih) dibandingkan dengan after4 atau after8. Saya masih berpendapat bahwa kehilangan beberapa siklus sekarang sepadan dengan keuntungan menggunakan API yang terdefinisi dengan baik. Dengan begitu, perbaikan apa pun di masa mendatang pada penerapan pola akses data tersebut akan menguntungkan kode Anda. for-loop terlalu umum untuk memiliki pengoptimalan yang berarti untuk pola yang sama
Ada opsi asinkron valid lainnya di luar after4 dan after8, seperti good01
Lint Kode Anda dan Terapkan Gaya
Kode tanpa gaya yang konsisten (tampilan dan nuansa) sangat sulit untuk dibaca dan dipahami. Oleh karena itu, aspek penting dalam penulisan kode kelas atas dalam bahasa apa pun adalah memiliki gaya yang konsisten dan masuk akal. Karena luasnya ekosistem JS, ada BANYAK pilihan untuk linter dan spesifikasi gaya. Apa yang saya tidak bisa cukup tekankan adalah bahwa jauh lebih penting bahwa Anda menggunakan linter dan menerapkan gaya (salah satunya) daripada linter/gaya mana yang Anda pilih secara khusus. Pada akhirnya, tidak ada yang akan menulis kode persis seperti yang saya inginkan, jadi mengoptimalkannya adalah tujuan yang tidak realistis
Saya melihat banyak orang bertanya apakah mereka harus menggunakan eslint atau lebih cantik. Bagi saya, mereka melayani tujuan yang sangat berbeda dan oleh karena itu harus digunakan bersamaan. Eslint adalah linter tradisional hampir sepanjang waktu. Ini akan mengidentifikasi masalah dengan kode Anda yang kurang berkaitan dengan gaya dan lebih berkaitan dengan kebenaran. Misalnya, saya menggunakan eslint dengan aturan AirBNB. Dengan konfigurasi tersebut, kode berikut akan memaksa linter gagal
Seharusnya cukup jelas bagaimana eslint menambah nilai pada siklus pengembangan Anda. Intinya, ini memastikan Anda mengikuti aturan tentang praktik yang baik dan tidak. Karena itu, linter secara inheren berpendirian. Seperti semua pendapat, ambillah dengan sebutir garam. Linter bisa salah
Prettier adalah pemformat kode. Itu kurang peduli dengan kebenaran dan jauh lebih khawatir tentang keseragaman dan konsistensi. Prettier tidak akan mengeluh tentang penggunaan function myAPI(someString, someNum) {
if (someNum > 0) {
leakCredentials();
} else {
console.log(someString);
}
}5, tetapi secara otomatis akan menyelaraskan semua tanda kurung dalam kode Anda. Dalam proses pengembangan pribadi saya, saya selalu menjalankan lebih cantik sebagai langkah terakhir sebelum memasukkan kode ke Git. Dalam banyak kasus, masuk akal untuk menjalankan Prettier secara otomatis pada setiap komit ke repo. Ini memastikan bahwa semua kode yang masuk ke kontrol sumber memiliki gaya dan struktur yang konsisten
Uji Kode Anda
Tes penulisan adalah metode tidak langsung namun sangat efektif untuk meningkatkan kode JS yang Anda tulis. Saya merekomendasikan untuk membiasakan diri dengan beragam alat pengujian. Kebutuhan pengujian Anda akan bervariasi, dan tidak ada alat tunggal yang dapat menangani semuanya. Ada banyak sekali alat pengujian yang mapan di ekosistem JS, jadi memilih alat sebagian besar bergantung pada selera pribadi. Seperti biasa, pikirkan sendiri