Cara mengubah objek yang tidak dapat diubah di javascript

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

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 Abadi

Pertimbangkan fungsi yang menambah properti .counter dari suatu objek

const obj1 = { bools: [true, false], counter: 7 } const obj2 = { bools: [false, true], counter: 3 } function incrementObjCounter (obj) { // naive solution const newObj = { bools: obj.bools, // same reference counter: obj.counter + 1 // updated data } return newObj } const newObj1 = incrementObjCounter(obj1) const newObj2 = incrementObjCounter(obj2) console.log(obj1, newObj1) // different objects console.log(obj2, newObj2) // different objects console.log(obj1.bools === newObj1.bools) // shared reference console.log(obj2.bools === newObj2.bools) // shared reference

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

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
0 di atas. Pembagian struktural ini berarti pembaruan yang tidak dapat diubah seringkali dapat menggunakan kembali memori yang ada

Sayangnya, kode di atas rapuh; . Fungsi ini tidak dapat digunakan kembali untuk bentuk objek lain atau mudah dipelihara

Kesulitan dengan Pembaruan Abadi

Misalkan kita memiliki beberapa data bersarang sebagai berikut

const dino = {
  name: 'Denver',
  type: 'dinosaur',
  friends: [
    {
      name: 'Wally',
      type: 'human',
      pets: [
        {
          name: 'Rocky',
          type: 'dog'
        }
      ]
    },
    {
      name: 'Casey',
      type: 'human'
    }
  ]
}
_

Masuk ke mode layar penuh Keluar dari mode layar penuh

Mungkin kita mungkin ingin memberi Wally kelinci peliharaan baru dalam bentuk

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
1. Cobalah; . Tidak seperti kebanyakan aplikasi dunia nyata, kami memiliki
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
2 sehingga Anda akan tahu jika Anda secara tidak sengaja mencoba untuk mengubahnya – kesalahan akan terjadi

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
4
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
5

Apakah 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?

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
6
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
7

Tinjauan Perilaku Vanilla JS

Primitif 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

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
_8

Tentu saja, JavaScript memungkinkan penugasan ulang variabel bersama, yang tidak sama dengan mutasi tetapi menghadirkan banyak masalah yang sama

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
_9

Coba ubah istilah dalam ekspresi di atas ke

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
0 dan jalankan kembali kode. Karena ketegasan yang melekat dalam pengikatan kembali variabel
const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
1 bersama, kami mendapatkan hasil yang berbeda, mengkhianati harapan kami tentang cara kerja penjumlahan

Objek 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

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
2 deklarasi akan mencegah mutasi. Tidak begitu;

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_4

JavaScript memang memiliki metode statis

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_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
const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_6 sendiri memutasi objek. )

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_7

Namun berhati-hatilah –

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_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

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)
_9

Membekukan 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

  • seorang penulis perlu memprediksi bahwa objek yang diberikan sepadan dengan usaha ekstra untuk membekukan, memungkiri prinsip YAGNI
  • algoritme deep freeze, seperti algoritme klon dalam, memiliki kasus tepi dan jebakan di JS (mis. g. siklus, mendeteksi objek dengan benar)
  • membekukan objek bersarang dalam secara rekursif mungkin menjadi masalah kinerja (valid atau tidak)
Pendekatan 1. Pembaruan Asli yang Tidak Dapat Diubah

Jadi, alat apa yang disediakan JS ketika berharap untuk menghindari mutasi? . Untuk itu, kami memiliki beberapa metode bawaan

Sebarkan Sintaks

Pada ES2015, operator spread

produce(currentState, producer: (draftState) => void): nextState
_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

produce(currentState, producer: (draftState) => void): nextState
3

Dengan objek khususnya, ini memungkinkan pembaruan kunci tingkat atas dengan mudah

produce(currentState, producer: (draftState) => void): nextState
4

Di beberapa lingkungan, sintaks penyebaran objek mungkin tidak didukung, tetapi

produce(currentState, producer: (draftState) => void): nextState
5 (juga ES2015) mungkin tersedia

Masalah dengan Metode Asli

Spread 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

produce(currentState, producer: (draftState) => void): nextState
6ing atau
produce(currentState, producer: (draftState) => void): nextState
7ing

produce(currentState, producer: (draftState) => void): nextState
8

Saat memperbarui elemen array dapat dilakukan dengan

produce(currentState, producer: (draftState) => void): nextState
7 atau
const vireo = a => b => f => f(a)(b)

0

const vireo = a => b => f => f(a)(b)

1

Metode 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

const vireo = a => b => f => f(a)(b)

2
const vireo = a => b => f => f(a)(b)

3

Ini berfungsi, tetapi memiliki beberapa kelemahan penting

  • itu rumit dan cukup bertele-tele sehingga sulit untuk dibaca, ditulis, dan dipelihara
    • pergantian yang sering antara larik dan penyebaran objek membuatnya mudah kehilangan jejak tentang apa yang terjadi
    • semakin dalam objek yang dituju, semakin tinggi kemungkinan rantai referensi tumbuh (mis. g.
      const vireo = a => b => f => f(a)(b)
      
      
      4)
    • memodifikasi satu nilai tertentu dalam array membutuhkan penambahan
      const vireo = a => b => f => f(a)(b)
      
      
      0 atau
      produce(currentState, producer: (draftState) => void): nextState
      
      7 ke dalam campuran, meningkatkan kebisingan
    • setiap kesalahan dalam implementasi akan menjadi kesalahan mutasi diam dalam praktiknya, kecuali
      const { view } = require('ramda')
      const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
      
      2 sangat beku dan Anda dalam mode ketat
  • kinerjanya tidak ideal
    • sintaks spread memanggil protokol iterasi
    • membuat salinan objek/array yang diubah (mis. g.
      const vireo = a => b => f => f(a)(b)
      
      
      8 dan
      const vireo = a => b => f => f(a)(b)
      
      
      9 array) adalah lens0 kompleksitas waktu
Pendekatan 2.

Konteks

Lensa 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 tinggi

Secara konseptual, lensa adalah sepasang fungsi

  • pengambil untuk elemen "fokus" lens2 yang dapat diturunkan dari beberapa data "sumber" lens3
  • setter yang dapat memperbarui lens_2, secara permanen menghasilkan lens3 baru

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;

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)

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)

const { compose } = require('ramda')
const deepLens = compose(outerLens, innerLens)

Masuk ke mode layar penuh Keluar dari mode layar penuh

Demonstrasi

Paling umum dengan Ramda, seseorang tidak akan menulis lensa dari awal tetapi menggunakan metode seperti dan untuk menghasilkan lensa yang benar

const vireo = a => b => f => f(a)(b)

2 immer2

Metode lens7 dan lens8 menghasilkan data baru dengan pembaruan yang diinginkan, tetapi seperti Object/Array spread, nilai baru juga berbagi referensi yang tidak diperbarui dengan nilai lama

Semua ini baik dan bagus, tetapi bagaimana dengan data bersarang?

const vireo = a => b => f => f(a)(b)

2 immer6

Dibandingkan dengan teknik Object/Array spread + map kami, di atas jauh lebih ringkas, deklaratif, mudah, dan sangat mudah. Tapi itu menjadi lebih mudah;

const vireo = a => b => f => f(a)(b)

2 immer9

Anda dapat menggunakan lensa ini dengan lens_6 dan lens8 juga, tentunya – coba sendiri. Pada akhirnya, solusi akhir kami telah diringkas menjadi dua baris yang mudah dibaca – satu menyusun lensa dari jalur, yang lain menggunakan lensa untuk menyetel properti bersarang dalam secara permanen di beberapa data

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;

immutable_2

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. Benamkan

Beberapa 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 (immutable3), metode tidak aman (immutable4), dan kasus lain – dalam bentuk metaprogramming. Secara khusus, ini menggunakan konstruktor immutable_5 untuk membuat versi "draf" dari objek asli Anda. Anda dapat mengotak-atik draf ini sesuka hati, bahkan memutasi referensi bersarang dalam secara langsung. Alih-alih membiarkan perubahan itu benar-benar terjadi, Immer mengubahnya menjadi pembaruan agregat yang tidak dapat diubah pada objek aslinya

Demonstrasi

Fungsi inti Immer, immutable6, memiliki tanda tangan berikut

produce(currentState, producer: (draftState) => void): nextState

Masuk ke mode layar penuh Keluar dari mode layar penuh

immutable_7

Seperti yang ditunjukkan immutable8 terakhir di atas, Immer menggunakan pembagian struktural seperti penyebaran manual dan solusi lensa Ramda kami. Keuntungannya antara lain

  • properti baru secara otomatis dibekukan dalam mode pengembangan
  • immutable_6 memiliki versi kari untuk aplikasi parsial
  • immer adalah perpustakaan kecil dengan tujuan tunggal
  • tidak perlu mempelajari API baru (selain dari pembungkus satu fungsi)
  • tidak perlu berhati-hati dalam banyak kasus, lebih sedikit biaya kognitif

Yang Anda Butuhkan hanyalah Immer?

Meskipun Immer sangat apik, ia pada akhirnya melakukan operasi

produce(currentState, producer: (draftState) => void): nextState
5 / spread / map yang sama seperti solusi kami sebelumnya. Jadi, sementara Immer mendapat manfaat dari pembagian struktural, beberapa pembaruan pada data besar mungkin lambat (lens0 untuk menyalin array, misalnya). Juga, , bukan built-in lain seperti .counter3 dan .counter4. Untuk jebakan lainnya,

Pendekatan 4. Kekal. js

Tiga pendekatan yang telah kita periksa sejauh ini semuanya berfokus pada struktur data yang pada dasarnya sama – bersarang

produce(currentState, producer: (draftState) => void): nextState
0s dan
produce(currentState, producer: (draftState) => void): nextState
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 itu

Namun, 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 lens0. Jika Anda gagal berhati-hati, Anda dapat memanggil metode asli seperti .counter9 tanpa menyadarinya mengubah struktur data asli

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 const obj1 = { bools: [true, false], counter: 7 } const obj2 = { bools: [false, true], counter: 3 } function incrementObjCounter (obj) { // naive solution const newObj = { bools: obj.bools, // same reference counter: obj.counter + 1 // updated data } return newObj } const newObj1 = incrementObjCounter(obj1) const newObj2 = incrementObjCounter(obj2) console.log(obj1, newObj1) // different objects console.log(obj2, newObj2) // different objects console.log(obj1.bools === newObj1.bools) // shared reference console.log(obj2.bools === newObj2.bools) // shared reference0 dan .counter3 (berbeda dari kelas ES2015 dengan nama yang sama). Untuk data dalam jumlah besar, entitas ini sekarang lebih cepat dimodifikasi;

Demonstrasi

Immutable 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 .counter3 dan const obj1 = { bools: [true, false], counter: 7 } const obj2 = { bools: [false, true], counter: 3 } function incrementObjCounter (obj) { // naive solution const newObj = { bools: obj.bools, // same reference counter: obj.counter + 1 // updated data } return newObj } const newObj1 = incrementObjCounter(obj1) const newObj2 = incrementObjCounter(obj2) console.log(obj1, newObj1) // different objects console.log(obj2, newObj2) // different objects console.log(obj1.bools === newObj1.bools) // shared reference console.log(obj2.bools === newObj2.bools) // shared reference0

const vireo = a => b => f => f(a)(b)

2
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
01

Beberapa keunggulan tidak secara langsung ditunjukkan, tetapi perlu diperhatikan

  • jika properti tidak ada,
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    _02 akan melakukan hubungan pendek dan mengembalikan
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    03 daripada melempar kesalahan
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    04
  • jika properti tidak ada,
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    _05 akan menghasilkan .counter3 objek baru di sepanjang jalan
  • masih banyak lagi yang bisa kita lakukan dengan Immutable. js .counter3s dan const obj1 = { bools: [true, false], counter: 7 } const obj2 = { bools: [false, true], counter: 3 } function incrementObjCounter (obj) { // naive solution const newObj = { bools: obj.bools, // same reference counter: obj.counter + 1 // updated data } return newObj } const newObj1 = incrementObjCounter(obj1) const newObj2 = incrementObjCounter(obj2) console.log(obj1, newObj1) // different objects console.log(obj2, newObj2) // different objects console.log(obj1.bools === newObj1.bools) // shared reference console.log(obj2.bools === newObj2.bools) // shared reference0s, seperti
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    09,
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    10,
    const { view } = require('ramda')
    const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
    
    11, dll. – semuanya dengan cara yang murni dan tidak merusak

Pada akhirnya, jika beberapa pustaka atau kerangka kerja lain memerlukan objek atau larik JS biasa, kita dapat menggunakan

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
12 untuk mengubahnya kembali

Yang Anda Butuhkan Tidak Berubah?

Performa dan keamanan adalah alasan utama untuk menggunakan immutable, tetapi seperti biasa, ada pengorbanan yang perlu dipertimbangkan

  • Tidak dapat diubah mengharuskan pengembang untuk mempelajari API baru yang relatif besar
  • Men-debug nilai immutable kurang ergonomis dibandingkan objek dan larik biasa
  • Proyek yang menggunakan kelas Immutable mungkin perlu dikonversi ke dan dari tipe data yang lebih sederhana, yang berpotensi merusak manfaat kinerja
Aku ingin semua

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 lens8, atau di dalam panggilan ke fungsi immutable6 Immer. Mengetahui berbagai pendekatan memungkinkan Anda meraih abstraksi yang sesuai dengan kasus penggunaan Anda

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,

const vireo = a => b => f => f(a)(b)

0 /
produce(currentState, producer: (draftState) => void): nextState
6 /
produce(currentState, producer: (draftState) => void): nextState
7 untuk memperbarui atau menghapusRamda Bawaan (lensa) · (52 kB


Tambahan. Kekekalan melalui Pengodean Fungsi

Prof. Dierk König menampilkan teknik lain untuk data yang tidak dapat diubah. menyimpan data Anda sebagai fungsi

Cara mengubah objek yang tidak dapat diubah di javascript

Dierk Konig

@mittie

@g_lebec bagus, seperti biasa. Cara pilihan saya untuk kekekalan adalah dengan menggunakan Vireo dan generalisasinya, Tuple(n). Analog untuk tipe Sum. Juga, saya menghindari ketergantungan apa pun

09. 36.00 - 13 Februari 2019

Kombinator Vireo (seperti namanya dalam buku terkenal Raymond Smullyan, To Mock a Mockingbird) didefinisikan dalam JavaScript sebagai berikut

const vireo = a => b => f => f(a)(b)

Masuk ke mode layar penuh Keluar dari mode layar penuh

Ketika

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
_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

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
24
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
25

Perhatikan 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?

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
24
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
27

Untuk membuatnya lebih mudah, kita bisa menulis fungsi pembantu untuk mendapatkan, menyetel, dan memetakan…

const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
24
const { view } = require('ramda')
const extractedData = view(someLens, sourceData) // NOT someLens(wrappingData)
29

Menyimpan 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 . .