Teknik pemrograman fungsional dan pola desain semakin populer di aplikasi JavaScript. Alat dan kerangka kerja seperti RxJS, Redux, dan React mengambil inspirasi dari pemrograman reaktif fungsional, misalnya Show
Di antara banyak fitur FP yang beragam, aspek kemurnian, kekekalan, dan struktur data yang saling terkait menghasilkan manfaat seperti transparansi referensial dan idempotensi. Sederhananya, menghindari mutasi dan efek samping membuat kode lebih mudah untuk dipikirkan dan disusun JavaScript adalah bahasa pemrograman. Ini memungkinkan pendekatan fungsional, tetapi tanpa kenyamanan atau jaminan yang ditemukan dalam beberapa bahasa fungsional khusus. Oleh karena itu, pengembang JS harus sangat berhati-hati jika ingin memperbarui data tanpa mutasi. Misalnya, peredam Redux "tidak boleh mengubah argumennya, melakukan efek samping..., [atau] memanggil fungsi non-murni", tetapi Redux sendiri tidak menyediakan alat untuk mematuhi batasan tersebut Artikel ini tidak akan fokus pada manfaat dari kekekalan, melainkan mengeksplorasi bagaimana hal itu dapat dicapai di JS jika diinginkan Apa itu Pembaruan AbadiPertimbangkan fungsi yang menambah properti
Daripada memodifikasi objek aslinya, kami membuat objek baru dengan data yang diubah. Perhatikan bahwa solusi yang lebih naif mungkin secara rekursif menyalin semua data di objek asli. Ini biasanya disebut sebagai , dan menggandakan jumlah memori yang digunakan Namun, jika kami tidak pernah bermaksud untuk memutasikan data di objek dasar kami, data tersebut dapat dibagikan dengan aman ke objek baru, seperti yang ditunjukkan dengan properti 0 di atas. Pembagian struktural ini berarti pembaruan yang tidak dapat diubah seringkali dapat menggunakan kembali memori yang adaSayangnya, kode di atas rapuh; . Fungsi ini tidak dapat digunakan kembali untuk bentuk objek lain atau mudah dipelihara Kesulitan dengan Pembaruan AbadiMisalkan kita memiliki beberapa data bersarang sebagai berikut _Masuk ke mode layar penuh Keluar dari mode layar penuh Mungkin kita mungkin ingin memberi Wally kelinci peliharaan baru dalam bentuk 1. Cobalah; . Tidak seperti kebanyakan aplikasi dunia nyata, kami memiliki 2 sehingga Anda akan tahu jika Anda secara tidak sengaja mencoba untuk mengubahnya – kesalahan akan terjadi 4 5Apakah Anda berhasil menghasilkan dinosaurus baru dengan benar, tanpa melakukan kesalahan terkait mutasi? Untuk tantangan ini, kami meminta Anda menambahkan objek. Bagaimana dengan memperbarui objek bersarang dalam, atau menghapusnya? 6 7Tinjauan Perilaku Vanilla JSPrimitif tidak dapat diubah (tetapi juga dapat ditetapkan kembali)Primitif JavaScript (nilai tidak terdefinisi, null, boolean, angka, string, dan simbol) tidak dapat diubah secara default, jadi Anda sudah dicegah untuk mengubah nilai primitif itu sendiri secara permanen _8Tentu saja, JavaScript memungkinkan penugasan ulang variabel bersama, yang tidak sama dengan mutasi tetapi menghadirkan banyak masalah yang sama _9Coba ubah istilah dalam ekspresi di atas ke 0 dan jalankan kembali kode. Karena ketegasan yang melekat dalam pengikatan kembali variabel 1 bersama, kami mendapatkan hasil yang berbeda, mengkhianati harapan kami tentang cara kerja penjumlahanObjek dapat Diubah (tetapi juga Dapat Dibekukan)Apa pun yang bukan tipe primitif yang tercantum di atas dianggap sebagai objek di JS – termasuk array dan fungsi. Kesalahpahaman pemula yang umum dengan JavaScript adalah bahwa 2 deklarasi akan mencegah mutasi. Tidak begitu; _4JavaScript memang memiliki metode statis _5 untuk mencegah penugasan ulang properti. Dalam mode ketat, mencoba menyetel properti pada objek yang dibekukan menimbulkan kesalahan yang membantu. Sayangnya, dalam mode non-ketat, upaya tersebut hanya ditolak secara diam-diam. (Perhatikan bahwa _6 sendiri memutasi objek. ) _7Namun berhati-hatilah – _6 hanya mencegah penugasan ulang properti tingkat atas, dan tidak mencegah mutasi objek yang dirujuk di properti tersebut. Untuk itu, Anda memerlukan algoritme "" non-asli yang membekukan objek secara rekursif _9Membekukan objek dapat membantu mengungkap dan men-debug kesalahan dengan gagal lebih awal (setelah upaya mutasi) daripada terlambat (ketika keadaan yang tidak terduga menghasilkan perilaku yang salah), setidaknya dalam mode ketat. Namun dalam praktiknya, pembekuan tidak terlalu umum, mungkin karena
Jadi, alat apa yang disediakan JS ketika berharap untuk menghindari mutasi? . Untuk itu, kami memiliki beberapa metode bawaan Sebarkan SintaksPada ES2015, operator spread _2 telah memungkinkan untuk memperluas nilai iterable menjadi argumen eksplisit. Saat digunakan di dalam array baru atau literal objek, ini memungkinkan penyalinan properti dari nilai yang ada ke dalam nilai baru 3Dengan objek khususnya, ini memungkinkan pembaruan kunci tingkat atas dengan mudah 4Di beberapa lingkungan, sintaks penyebaran objek mungkin tidak didukung, tetapi 5 (juga ES2015) mungkin tersediaMasalah dengan Metode AsliSpread memudahkan penambahan nilai baru ke array, menambahkan key-value pair baru ke objek, atau memodifikasi key-value pair pada objek. Namun, penghapusan key-value pair tidak semudah itu. Ada beberapa trik menggunakan destrukturisasi tetapi tidak nyaman saat bekerja dengan objek bersarang Dalam hal array, menghapus elemen tertentu dapat dilakukan melalui 6ing atau 7ing 8Saat memperbarui elemen array dapat dilakukan dengan 7 atau 0 1Metode ini bekerja tetapi agak rumit dan rawan kesalahan. Dan kami hanya berurusan dengan data dangkal; . Mari kita kembali ke contoh asli dari artikel ini, menambahkan kelinci ke daftar hewan peliharaan teman Denver, Wally. Berikut adalah salah satu solusi yang mungkin menggunakan vanilla JS 2 3Ini berfungsi, tetapi memiliki beberapa kelemahan penting
KonteksLensa adalah pendekatan untuk berinteraksi dengan data bersarang yang berasal dari Haskell, bahasa fungsional yang malas (dan karena itu murni) dengan pengetikan statis yang disimpulkan Hindley-Milner. Dalam bahasa itu, mereka adalah alat yang sangat fleksibel dan umum yang bekerja di berbagai tipe data dengan mulus Lensa dan alat terkait adalah topik yang mendalam, dan dalam JavaScript kami tidak memiliki batasan jenis yang membuat lensa sangat berharga di Haskell. Namun, ide intinya sederhana dan cukup berguna sehingga muncul di Ramda, pustaka utilitas JS fungsional yang populer Level tinggiSecara konseptual, lensa adalah sepasang fungsi
Dalam hal implementasi, lensa sebenarnya adalah satu fungsi yang menjalankan salah satu dari tugas di atas, tergantung bagaimana lensa itu digunakan. Fungsi lensa tertentu tidak dipanggil langsung pada sumbernya;
Masuk ke mode layar penuh Keluar dari mode layar penuh Karena lensa adalah fungsi individual, mereka dapat langsung disusun bersama untuk membentuk lensa baru (contoh pola kombinator)
Masuk ke mode layar penuh Keluar dari mode layar penuh DemonstrasiPaling umum dengan Ramda, seseorang tidak akan menulis lensa dari awal tetapi menggunakan metode seperti dan untuk menghasilkan lensa yang benar 2 immer 2Metode Semua ini baik dan bagus, tetapi bagaimana dengan data bersarang? 2 immer 6Dibandingkan dengan teknik Object/Array spread + map kami, di atas jauh lebih ringkas, deklaratif, mudah, dan sangat mudah. Tapi itu menjadi lebih mudah; 2 immer 9Anda dapat menggunakan lensa ini dengan Yang Anda Butuhkan hanyalah Lensa?Lenses, menjadi fungsi yang dapat dikomposisi vanilla dengan banyak kemampuan (baca/perbarui/set), adalah solusi yang sangat fungsional dengan semua trik yang menyertainya. aplikasi parsial, penggunaan tingkat tinggi, portabilitas kelas satu, perilaku dinamis, dll. Namun, meskipun fungsi lensa Ramda memudahkan untuk melihat, menyetel, dan memetakan properti bersarang, menghapus sedikit lebih tidak langsung;
Instansiasi dasar dan penggunaan lensa melalui perpustakaan seperti Ramda mungkin cukup sederhana, tetapi pengguna harus menyadari bahwa lensa dan "optik" lainnya memiliki beberapa kegunaan tingkat lanjut dan manfaat penting. Misalnya, fungsi mirip lensa yang melanggar dapat mengakibatkan kasus tepi ganjil *(Subkumpulan lensa adalah isomorfisme, yang memungkinkan Anda untuk beroperasi pada struktur data seolah-olah mereka adalah struktur data lain (setara). Ada juga prisma, yang memungkinkan interaksi dengan data yang mungkin atau mungkin tidak berbentuk tertentu, dan traversal, yang dapat berfokus pada beberapa subbagian sekaligus. ) Pendekatan 3. BenamkanBeberapa pengembang mungkin lebih suka pendekatan yang lebih idiomatis daripada pembaruan yang tidak dapat diubah di JS yang tidak melibatkan pengimporan rangkaian alat yang lebih besar atau mempelajari API baru. Perpustakaan Michael Westrate, Immer, dibuat khusus hanya untuk itu Trik cerdas Immer adalah mengubah apa yang biasanya berarti tindakan bermutasi – mencegat penugasan ( DemonstrasiFungsi inti Immer,
Masuk ke mode layar penuh Keluar dari mode layar penuh
Seperti yang ditunjukkan
Yang Anda Butuhkan hanyalah Immer?Meskipun Immer sangat apik, ia pada akhirnya melakukan operasi 5 / spread / map yang sama seperti solusi kami sebelumnya. Jadi, sementara Immer mendapat manfaat dari pembagian struktural, beberapa pembaruan pada data besar mungkin lambat (lens 0 untuk menyalin array, misalnya). Juga, , bukan built-in lain seperti .counter 3 dan .counter 4. Untuk jebakan lainnya,Pendekatan 4. Kekal. jsTiga pendekatan yang telah kita periksa sejauh ini semuanya berfokus pada struktur data yang pada dasarnya sama – bersarang 0s dan 1s. Metode asli, lensa, dan immer semua "bermain dengan baik dengan orang lain" dalam arti bahwa perpustakaan dan basis kode lain umumnya akan menyediakan dan mengharapkan tipe data seperti ituNamun, salah satu kelemahan dari struktur tersebut adalah bahwa struktur tersebut tidak sengaja dirancang dengan mempertimbangkan pembaruan yang tidak dapat diubah. Untuk menambahkan item ke array, Anda harus membuat salinan array dengan semua elemen lainnya – operasi Pendekatan alternatif adalah beralih ke struktur data yang berfungsi murni (tautan PDF), atau setidaknya struktur data dengan operasi pembaruan yang tidak dapat diubah secara efisien. Struktur data seperti itu akan memaparkan API hanya dari operasi murni, mengurangi kemungkinan kesalahan Pustaka Facebook Immutable memanfaatkan struktur semacam itu. Meskipun diimplementasikan dalam vanilla JavaScript, detailnya disembunyikan dari pengguna, yang sekarang berinteraksi dengan kelas seperti DemonstrasiImmutable adalah perpustakaan besar dengan banyak kelas, yang masing-masing memiliki banyak metode. Kami akan mendemonstrasikan hanya sebagian kecil dari perpustakaan itu, dimulai dengan mengonversi beberapa data JS tipikal menjadi rangkaian bersarang 2 01Beberapa keunggulan tidak secara langsung ditunjukkan, tetapi perlu diperhatikan
Pada akhirnya, jika beberapa pustaka atau kerangka kerja lain memerlukan objek atau larik JS biasa, kita dapat menggunakan 12 untuk mengubahnya kembaliYang Anda Butuhkan Tidak Berubah?Performa dan keamanan adalah alasan utama untuk menggunakan
Setiap pendekatan yang ditelaah memiliki pro dan kontra, seperti yang sering terjadi dalam pemrograman. Untungnya, mereka tidak saling eksklusif. Misalnya, Brian Lonsdorf mendemonstrasikan penggunaan lensa Ramda untuk memadukan objek/array asli dengan Immutable. peta/daftar js dalam artikelnya Lenses with Immutable. js. Demikian pula, seseorang dapat menggunakan penyebaran array di dalam fungsi yang diteruskan ke Ramda ToolLearning CurveSafetyPerformanceConceptTL;DRNativeJS devs diharapkan untuk mengetahuinya, tetapi membutuhkan waktu untuk menguasai Sumber kesalahan yang buruk dan umum Lambat (linear) untuk data dalam jumlah besarSalin properti bersarang secara manual, 0 / 6 / 7 untuk memperbarui atau menghapusRamda Bawaan (lensa) · (52 kB Tambahan. Kekekalan melalui Pengodean FungsiProf. Dierk König menampilkan teknik lain untuk data yang tidak dapat diubah. menyimpan data Anda sebagai fungsi
Kombinator Vireo (seperti namanya dalam buku terkenal Raymond Smullyan, To Mock a Mockingbird) didefinisikan dalam JavaScript sebagai berikut
Masuk ke mode layar penuh Keluar dari mode layar penuh Ketika _23 diterapkan dua kali (ke dua bagian data yang terpisah), ia mengembalikan fungsi akhir yang telah menutup kedua titik data. Hasilnya secara konseptual adalah sepasang angka, disimpan sebagai penutupan fungsi. Untuk mengakses nomor yang disimpan, argumen fungsi final disediakan 24 25Perhatikan bahwa dengan contoh ini, tidak ada cara untuk benar-benar mengubah nilai setelah pasangan dibuat – penutupan bertindak sebagai bentuk data istimewa. Bagaimana kita bisa "memperbarui" pasangan seperti itu? 24 27Untuk membuatnya lebih mudah, kita bisa menulis fungsi pembantu untuk mendapatkan, menyetel, dan memetakan… 24 29Menyimpan data melalui penutupan, dan membuat fungsi baru yang tunduk pada fungsi lama, dapat ditingkatkan ke data yang lebih kompleks dan juga dibuat lebih idiomatis di JS; . Jika pendekatan ini membuat Anda penasaran, Thinking with Types dari Sandy Maquire mengilustrasikan metode cerdas (dalam Haskell) untuk menyandikan papan Tic-Tac-Toe sebagai fungsi dari koordinat ke data. Bereksperimenlah dengan versi JS di bawah ini Bagaimana Anda mengubah objek yang tidak dapat diubah?Objek yang tidak dapat diubah adalah objek yang tidak berubah. Anda membuatnya, lalu Anda tidak dapat mengubahnya. Sebagai gantinya, jika Anda ingin mengubah objek yang tidak dapat diubah, Anda harus menggandakannya dan mengubah klon saat Anda membuatnya . Objek Java yang tidak dapat diubah harus memiliki semua bidangnya internal, bidang final pribadi.
Bagaimana cara membuat objek tidak berubah dalam JavaScript?Untuk membuat objek tidak dapat diubah, bekukan secara rekursif setiap properti non-primitif (deep freeze) .
Bagaimana Anda mengubah objek dalam JavaScript?Seseorang dapat memperbarui nilai properti hanya dengan menugaskan ulang nilainya ke kunci yang sama. . Tambahkan properti ke array Object. . Hapus properti dari array Object. . Perbarui setiap nilai properti yang ada dalam larik Objek Bagaimana Anda memodifikasi properti objek?Setelah Anda membuat objek, Anda dapat menyetel atau mengubah propertinya dengan memanggil properti secara langsung dengan operator titik (jika objek mewarisi dari IDL_Object) atau dengan memanggil nama objek . . |