Mengapa imutabilitas sangat penting (atau dibutuhkan) dalam JavaScript?


206

Saat ini saya sedang mengerjakan kerangka Bereaksi JS dan Bereaksi Asli . Di tengah jalan saya menemukan Immutability atau Immutable-JS library , ketika saya membaca tentang implementasi Flux dan Redux Facebook.

Pertanyaannya adalah, mengapa kekekalan begitu penting? Apa yang salah dalam bermutasi objek? Bukankah itu membuat hal-hal sederhana?

Sebagai contoh, mari kita pertimbangkan aplikasi pembaca Berita sederhana dengan layar pembuka menjadi tampilan daftar berita utama.

Jika saya mengatur mengatakan array objek dengan nilai awalnya saya tidak bisa memanipulasinya. Itulah yang dikatakan prinsip kekekalan, kan? (Koreksi saya jika saya salah.) Tetapi, bagaimana jika saya memiliki objek Berita baru yang harus diperbarui? Dalam kasus biasa, saya bisa saja menambahkan objek ke array. Bagaimana saya mencapai dalam kasus ini? Hapus toko dan buat kembali? Bukankah menambahkan objek ke array operasi yang lebih murah?



2
Struktur data yang tidak dapat berubah dan fungsi murni mengarah pada transparansi referensial, menjadikannya lebih mudah untuk mempertimbangkan perilaku program Anda. Anda juga mendapatkan backtracking gratis saat menggunakan struktur data fungsional.
WorBlux

Saya memberikan sudut pandang Redux @bozzmob.
prosti

1
Mungkin bermanfaat untuk belajar tentang keawetan secara umum sebagai konsep paradigma fungsional daripada mencoba berpikir bahwa JS ada hubungannya dengan itu. Bereaksi ditulis oleh penggemar pemrograman fungsional. Anda harus tahu apa yang mereka ketahui untuk memahaminya.
Gherman

Itu tidak perlu, tetapi menawarkan beberapa trade off yang bagus. Keadaan Mutable adalah ke Perangkat Lunak seperti Pindah Bagian ke Perangkat Keras
Kristian Dupont

Jawaban:


196

Saya baru-baru ini meneliti topik yang sama. Saya akan melakukan yang terbaik untuk menjawab pertanyaan Anda dan mencoba membagikan apa yang telah saya pelajari sejauh ini.

Pertanyaannya adalah, mengapa kekekalan begitu penting? Apa yang salah dalam bermutasi objek? Bukankah itu membuat hal-hal sederhana?

Pada dasarnya itu bermuara pada kenyataan bahwa ketidakmampuan meningkatkan prediktabilitas, kinerja (secara tidak langsung) dan memungkinkan untuk pelacakan mutasi.

Prediktabilitas

Mutasi menyembunyikan perubahan, yang menciptakan efek samping (tidak terduga), yang dapat menyebabkan bug jahat. Ketika Anda menerapkan imutabilitas, Anda dapat membuat arsitektur aplikasi dan model mental Anda tetap sederhana, yang membuatnya lebih mudah untuk mempertimbangkan aplikasi Anda.

Performa

Meskipun menambahkan nilai ke Objek yang tidak dapat diubah berarti bahwa instance baru perlu dibuat di mana nilai-nilai yang ada perlu disalin dan nilai-nilai baru perlu ditambahkan ke Objek baru yang menghabiskan memori, Objek yang tidak dapat diubah dapat menggunakan pembagian struktural untuk mengurangi memori. atas.

Semua pembaruan mengembalikan nilai baru, tetapi struktur internal dibagikan untuk secara drastis mengurangi penggunaan memori (dan meronta-ronta GC). Ini berarti bahwa jika Anda menambahkan vektor dengan 1000 elemen, itu tidak benar-benar membuat vektor baru 1001-elemen. Kemungkinan besar, secara internal hanya beberapa objek kecil yang dialokasikan.

Anda dapat membaca lebih lanjut tentang ini di sini .

Pelacakan Mutasi

Selain mengurangi penggunaan memori, immutability memungkinkan Anda untuk mengoptimalkan aplikasi Anda dengan memanfaatkan kesetaraan referensi dan nilai. Ini membuatnya sangat mudah untuk melihat apakah ada yang berubah. Misalnya perubahan status dalam komponen reaksi. Anda dapat menggunakan shouldComponentUpdateuntuk memeriksa apakah keadaan identik dengan membandingkan Objek keadaan dan mencegah rendering yang tidak perlu. Anda dapat membaca lebih lanjut tentang ini di sini .

Sumber daya tambahan:

Jika saya atur katakanlah array objek dengan nilai awalnya. Saya tidak bisa memanipulasi itu. Itulah yang dikatakan prinsip kekekalan, kan? (Koreksi saya jika saya salah). Tetapi, bagaimana jika saya memiliki objek Berita baru yang harus diperbarui? Dalam kasus biasa, saya bisa saja menambahkan objek ke array. Bagaimana saya mencapai dalam kasus ini? Hapus toko & buat ulang itu? Bukankah menambahkan objek ke array operasi yang lebih murah?

Ya ini benar. Jika Anda bingung bagaimana menerapkan ini dalam aplikasi Anda, saya akan merekomendasikan Anda untuk melihat bagaimana redux melakukan ini agar terbiasa dengan konsep inti, itu banyak membantu saya.

Saya suka menggunakan Redux sebagai contoh karena itu mencakup imutabilitas. Ini memiliki satu state state immutable (disebut sebagai store) di mana semua perubahan state secara eksplisit dengan mengirimkan tindakan yang diproses oleh peredam yang menerima status sebelumnya bersama dengan tindakan tersebut (satu per satu) dan mengembalikan status aplikasi Anda berikutnya . Anda dapat membaca lebih lanjut tentang prinsip inti di sini .

Ada kursus redux yang sangat baik di egghead.io di mana Dan Abramov , penulis redux, menjelaskan prinsip-prinsip ini sebagai berikut (saya sedikit memodifikasi kode agar lebih sesuai dengan skenario):

import React from 'react';
import ReactDOM from 'react-dom';

// Reducer.
const news = (state=[], action) => {
  switch(action.type) {
    case 'ADD_NEWS_ITEM': {
      return [ ...state, action.newsItem ];
    }
    default: {
        return state;
    }
  }
};

// Store.
const createStore = (reducer) => {
  let state;
  let listeners = [];

  const subscribe = (listener) => {
    listeners.push(listener);

    return () => {
      listeners = listeners.filter(cb => cb !== listener);
    };
  };

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach( cb => cb() );
  };

  dispatch({});

  return { subscribe, getState, dispatch };
};

// Initialize store with reducer.
const store = createStore(news);

// Component.
const News = React.createClass({
  onAddNewsItem() {
    const { newsTitle } = this.refs;

    store.dispatch({
      type: 'ADD_NEWS_ITEM',
      newsItem: { title: newsTitle.value }
    });
  },

  render() {
    const { news } = this.props;

    return (
      <div>
        <input ref="newsTitle" />
        <button onClick={ this.onAddNewsItem }>add</button>
        <ul>
          { news.map( ({ title }) => <li>{ title }</li>) }
        </ul>
      </div>
    );
  }
});

// Handler that will execute when the store dispatches.
const render = () => {
  ReactDOM.render(
    <News news={ store.getState() } />,
    document.getElementById('news')
  );
};

// Entry point.
store.subscribe(render);
render();

Juga, video-video ini menunjukkan secara lebih rinci cara mencapai ketidakberdayaan untuk:


1
@naomik terima kasih atas umpan baliknya! Tujuan saya adalah untuk mengilustrasikan konsep dan secara eksplisit menunjukkan bahwa Objek tidak bermutasi dan tidak perlu menunjukkan bagaimana menerapkannya sepanjang jalan. Namun, contoh saya mungkin agak membingungkan, saya akan memperbaruinya sedikit.
danillouz

2
@ bozzmob sama-sama Tidak, itu tidak benar, Anda harus memaksakan ketidakberdayaan dalam peredam sendiri. Ini berarti Anda dapat mengikuti strategi seperti yang ditunjukkan dalam video atau menggunakan perpustakaan seperti immutablejs. Anda dapat menemukan info lebih lanjut di sini dan di sini .
danillouz

1
@naomik ES6 constbukan tentang kekekalan. Mathias Bynens menulis artikel blog yang bagus tentang itu.
Lea Rosema

1
@terabaud terima kasih telah berbagi tautan. Saya setuju itu perbedaan penting. ^ _ ^
Terima kasih

4
Tolong jelaskan ini "Mutasi menyembunyikan perubahan, yang menciptakan efek samping (tidak terduga), yang dapat menyebabkan bug jahat. Ketika Anda menegakkan imutabilitas, Anda dapat menjaga arsitektur aplikasi dan model mental Anda sederhana, yang membuatnya lebih mudah untuk mempertimbangkan aplikasi Anda." Karena ini sama sekali tidak benar dalam konteks JavaScript.
Pavle Lekic

143

Pandangan Bertolak tentang Kekekalan

TL / DR: Ketidakmampuan lebih merupakan tren mode daripada kebutuhan dalam JavaScript. Jika Anda menggunakan Bereaksi itu memang memberikan pekerjaan yang rapi untuk beberapa pilihan desain yang membingungkan dalam manajemen negara. Namun dalam kebanyakan situasi lain, ia tidak akan menambah nilai lebih dari kerumitan yang diperkenalkannya, melayani lebih banyak untuk menopang resume daripada memenuhi kebutuhan klien yang sebenarnya.

Jawaban panjang: baca di bawah.

Mengapa imutabilitas sangat penting (atau dibutuhkan) dalam javascript?

Baiklah, saya senang Anda bertanya!

Beberapa waktu yang lalu seorang pria yang sangat berbakat bernama Dan Abramov menulis perpustakaan manajemen negara javascript yang disebut Redux yang menggunakan fungsi murni dan imutabilitas. Dia juga membuat beberapa video yang sangat keren yang membuat idenya sangat mudah dimengerti (dan dijual).

Waktunya sempurna. Kebaruan Angular memudar, dan dunia JavaScript siap untuk terpaku pada hal-hal terbaru yang memiliki tingkat keren yang tepat, dan perpustakaan ini tidak hanya inovatif tetapi ditempatkan dengan sempurna dengan React yang sedang dijajakan oleh pembangkit tenaga listrik Silicon Valley lainnya .

Sedih mungkin, mode memerintah di dunia JavaScript. Sekarang Abramov dipuji sebagai manusia setengah dewa dan kita semua manusia belaka harus tunduk pada Dao Kekekalan ... Apakah itu masuk akal atau tidak.

Apa yang salah dalam bermutasi objek?

Tidak ada!

Sebenarnya programmer telah bermutasi objek untuk er ... selama ada objek untuk bermutasi. Dengan kata lain, 50+ tahun pengembangan aplikasi.

Dan mengapa menyulitkan? Ketika Anda memiliki objek catdan mati, apakah Anda benar-benar membutuhkan sedetik catuntuk melacak perubahan? Kebanyakan orang hanya akan mengatakan cat.isDead = truedan selesai dengan itu.

Bukankah (bermutasi objek) membuat hal-hal sederhana?

IYA! .. Tentu saja!

Khususnya dalam JavaScript, yang dalam praktiknya paling berguna digunakan untuk menampilkan tampilan beberapa negara yang dikelola di tempat lain (seperti dalam database).

Bagaimana jika saya memiliki objek Berita baru yang harus diperbarui? ... Bagaimana saya mencapainya dalam kasus ini? Hapus toko & buat ulang itu? Bukankah menambahkan objek ke array operasi yang lebih murah?

Nah, Anda bisa pergi dengan pendekatan tradisional dan memperbarui Newsobjek, sehingga representasi dalam memori Anda dari objek itu berubah (dan tampilan yang ditampilkan kepada pengguna, atau jadi orang akan berharap) ...

Atau sebagai alternatif ...

Anda dapat mencoba pendekatan FP / Ketidakmampuan yang seksi dan menambahkan perubahan Anda ke Newsobjek ke array yang melacak setiap perubahan historis sehingga Anda dapat mengulangi melalui array dan mencari tahu apa yang seharusnya representasi negara yang benar (Fiuh!).

Saya mencoba mempelajari apa yang ada di sini. Tolong lakukan pencerahan saya :)

Mode datang dan pergi sobat. Ada banyak cara untuk menguliti kucing.

Saya menyesal Anda harus menanggung kebingungan dari serangkaian paradigma pemrograman yang terus berubah. Tapi hei, SELAMAT DATANG DI KLUB !!

Sekarang beberapa poin penting yang perlu diingat sehubungan dengan Kekekalan, dan Anda akan mendapatkan ini dilemparkan kepada Anda dengan intensitas demam yang hanya bisa dihabiskan oleh kenaifan.

1) Kekekalan luar biasa untuk menghindari kondisi balapan di lingkungan multi-utas .

Lingkungan multi-utas (seperti C ++, Java dan C #) bersalah atas praktik mengunci objek ketika lebih dari satu utas ingin mengubahnya. Ini buruk untuk kinerja, tetapi lebih baik daripada alternatif korupsi data. Namun tidak sebagus membuat semuanya abadi (Tuhan memuji Haskell!).

TAPI ALAS! Dalam JavaScript Anda selalu beroperasi pada satu utas . Bahkan pekerja web (masing-masing berjalan dalam konteks yang terpisah ). Jadi karena Anda tidak dapat memiliki kondisi ras terkait thread di dalam konteks eksekusi Anda (semua variabel global dan penutupan yang bagus), poin utama yang mendukung Immutability keluar dari jendela.

(Karena itu, ada adalah keuntungan untuk menggunakan fungsi murni pada pekerja web, yang adalah bahwa Anda akan memiliki harapan tentang mengutak-atik benda-benda di thread utama.)

2) Ketidakmampuan dapat (entah bagaimana) menghindari kondisi balapan di negara bagian aplikasi Anda.

Dan inilah inti masalahnya, sebagian besar pengembang (React) akan memberi tahu Anda bahwa Immutability dan FP dapat mengerjakan keajaiban ini yang memungkinkan keadaan aplikasi Anda dapat diprediksi.

Tentu saja ini tidak berarti bahwa Anda dapat menghindari kondisi balapan dalam basis data , untuk menarik yang satu dari itu Anda harus mengoordinasikan semua pengguna di semua browser , dan untuk itu Anda akan memerlukan teknologi push back-end seperti WebSockets ( lebih lanjut tentang ini di bawah ini) yang akan menyiarkan perubahan kepada semua orang yang menjalankan aplikasi.

Juga tidak berarti bahwa ada beberapa masalah inheren dalam JavaScript di mana status aplikasi Anda memerlukan ketetapan agar dapat diprediksi, pengembang mana pun yang telah mengkode aplikasi front-end sebelum Bereaksi akan memberi tahu Anda hal ini.

Klaim yang agak membingungkan ini hanya berarti bahwa dengan Bereaksi status aplikasi Anda akan menjadi lebih rentan terhadap kondisi balapan , tetapi ketidakberdayaan memungkinkan Anda untuk menghilangkan rasa sakit itu. Mengapa? Karena React adalah khusus .. telah dirancang sebagai perpustakaan rendering yang sangat dioptimalkan dengan manajemen keadaan yang koheren menempati posisi kedua, dan dengan demikian status komponen dikelola melalui rangkaian peristiwa yang tidak sinkron (alias "pengikatan data satu arah") yang tidak dapat Anda kendalikan lebih dan andalkan Anda ingat untuk tidak bermutasi secara langsung ...

Dengan konteks ini, mudah untuk melihat bagaimana kebutuhan untuk ketidakberdayaan tidak ada hubungannya dengan JavaScript dan banyak hubungannya dengan kondisi balapan di Bereaksi: jika memiliki banyak perubahan yang saling bergantung dalam aplikasi Anda dan tidak ada cara mudah untuk mengetahui apa keadaan Anda saat ini, Anda akan bingung , dan dengan demikian masuk akal untuk menggunakan keabadian untuk melacak setiap perubahan historis .

3) Kondisi lomba sangat buruk.

Yah, mereka mungkin jika Anda menggunakan Bereaksi. Tetapi mereka jarang terjadi jika Anda mengambil kerangka kerja yang berbeda.

Selain itu, Anda biasanya memiliki masalah yang jauh lebih besar untuk dihadapi ... Masalah seperti neraka ketergantungan. Seperti basis kode yang membengkak. Seperti CSS Anda tidak dimuat. Seperti proses pembangunan yang lambat atau terjebak pada back-end monolitik yang membuat iterasi hampir tidak mungkin. Seperti para dev yang tidak berpengalaman, tidak mengerti apa yang sedang terjadi dan mengacaukan banyak hal.

Kamu tahu. Realitas. Tapi hei, siapa yang peduli tentang itu?

4) Ketidakmampuan memanfaatkan Jenis Referensi untuk mengurangi dampak kinerja pelacakan setiap perubahan negara.

Karena serius, jika Anda akan menyalin hal-hal setiap kali keadaan Anda berubah, lebih baik Anda memastikan Anda pintar tentang hal itu.

5) Kekekalan memungkinkan Anda untuk melakukan hal-hal yang TIDAK .

Karena er .. ini adalah fitur nomor satu yang manajer proyek Anda akan minta, kan?

6) Keadaan tidak berubah memiliki banyak potensi keren dalam kombinasi dengan WebSockets

Last but not least, akumulasi delta negara membuat kasus yang cukup menarik dalam kombinasi dengan WebSockets, yang memungkinkan untuk konsumsi yang mudah dari negara sebagai aliran peristiwa abadi ...

Begitu uang jatuh pada konsep ini (negara menjadi aliran peristiwa - daripada set rekaman kasar mewakili pandangan terbaru), dunia abadi menjadi tempat ajaib untuk dihuni. Sebuah tanah event-bersumber heran dan kemungkinan bahwa melampaui waktu itu sendiri . Dan bila dilakukan dengan benar ini pasti dapat membuat real-time aplikasi easi er untuk mencapai, Anda hanya menyiarkan aliran acara untuk semua orang tertarik sehingga mereka dapat membangun representasi mereka sendiri dari sekarang dan menulis kembali perubahan mereka sendiri ke dalam aliran komunal.

Tetapi pada titik tertentu Anda bangun dan menyadari bahwa semua keajaiban dan keajaiban itu tidak datang secara gratis. Tidak seperti kolega Anda yang bersemangat, pemangku kepentingan Anda (ya, orang-orang yang membayar Anda) tidak terlalu peduli dengan filsafat atau mode dan banyak tentang uang yang mereka bayarkan untuk membangun produk yang dapat mereka jual. Dan intinya adalah bahwa lebih sulit untuk menulis kode abadi dan lebih mudah untuk memecahkannya, ditambah ada sedikit gunanya memiliki front-end abadi jika Anda tidak memiliki back-end untuk mendukungnya. Ketika (dan jika!) Anda akhirnya meyakinkan pemangku kepentingan Anda bahwa Anda harus mempublikasikan dan mengonsumsi acara melalui teknologi push seperti WebSockets, Anda akan tahu betapa sulitnya mengukur dalam produksi .


Sekarang untuk beberapa saran, sebaiknya Anda memilih untuk menerimanya.

Pilihan untuk menulis JavaScript menggunakan FP / Immutability juga merupakan pilihan untuk membuat basis kode aplikasi Anda lebih besar, lebih kompleks dan lebih sulit untuk dikelola. Saya akan sangat berpendapat untuk membatasi pendekatan ini untuk reduksi Redux Anda, kecuali jika Anda tahu apa yang Anda lakukan ... Dan JIKA Anda akan terus maju dan menggunakan immutability terlepas, maka terapkan keadaan tidak berubah ke seluruh tumpukan aplikasi Anda , dan bukan hanya sisi klien, karena Anda kehilangan nilai sebenarnya dari sebaliknya.

Sekarang, jika Anda cukup beruntung untuk dapat membuat pilihan dalam pekerjaan Anda, maka cobalah dan gunakan kebijaksanaan Anda (atau tidak) dan lakukan apa yang benar oleh orang yang membayar Anda . Anda dapat mendasarkan ini pada pengalaman Anda, pada usus Anda, atau apa yang terjadi di sekitar Anda (diakui jika semua orang menggunakan React / Redux maka ada argumen yang valid bahwa akan lebih mudah untuk menemukan sumber daya untuk melanjutkan pekerjaan Anda) .. Atau, Anda dapat mencoba baik Resume Driven Development atau Hype Driven Development . Mereka mungkin lebih seperti Anda.

Singkatnya, hal yang bisa dikatakan tentang kekekalan adalah bahwa hal itu akan membuat Anda modis dengan teman-teman Anda, setidaknya sampai kegilaan berikutnya muncul, pada titik mana Anda akan senang untuk melanjutkan.


Sekarang setelah sesi terapi diri ini saya ingin menunjukkan bahwa saya telah menambahkan ini sebagai artikel di blog saya => Kekekalan dalam JavaScript: A Contrarian View . Jangan ragu untuk menjawab di sana jika Anda memiliki perasaan yang kuat, Anda ingin melepaskan diri juga;).


10
Halo Steven, Ya. Saya memiliki semua keraguan ini ketika saya menganggap immutable.js dan redux. Tapi, jawaban Anda sungguh luar biasa! Ini menambah banyak nilai dan terima kasih untuk mengatasi setiap titik yang saya ragu. Ini jauh lebih jelas / lebih baik sekarang bahkan setelah bekerja selama berbulan-bulan pada objek yang tidak dapat diubah.
bozzmob

5
Saya telah menggunakan React with Flux / Redux selama lebih dari dua tahun dan saya sangat setuju dengan Anda, respons yang bagus!
Pavle Lekic

6
Saya sangat curiga bahwa pandangan tentang ketidakmampuan berkorelasi agak rapi dengan ukuran tim dan basis kode, dan saya pikir itu bukan kebetulan bahwa pendukung utama adalah raksasa lembah silikon. Yang sedang berkata, saya dengan hormat tidak setuju: ketidakmampuan adalah disiplin yang bermanfaat seperti tidak menggunakan kebotakan adalah disiplin yang berguna. Atau pengujian unit. Atau TDD. Atau analisis tipe statis. Tidak berarti Anda melakukannya sepanjang waktu, setiap saat (meskipun beberapa melakukannya). Saya juga akan mengatakan bahwa utilitas ortogonal untuk hype: dalam matriks yang berguna / berlebihan dan seksi / membosankan ada banyak contoh masing-masing."hyped" !== "bad"
Jared Smith

4
Hai @ftor, bagus, mengambil hal-hal yang terlalu jauh ke arah lain. Namun karena ada banyak sekali artikel dan argumen 'pro-imutabilitas dalam javascript', saya merasa saya perlu menyeimbangkan semuanya. Jadi pemula memiliki sudut pandang yang berlawanan untuk membantu mereka membuat keputusan.
Steven de Salas

4
Informatif, dan berjudul dengan gemilang. Sampai saya menemukan jawaban ini, saya pikir saya adalah satu-satunya yang memegang pandangan serupa. Saya mengenali nilai ketetapan, tetapi yang menggangguku adalah bahwa itu menjadi dogma yang menindas dengan teknik lainnya (misalnya, dengan merusak pengikatan 2 arah yang sangat berguna untuk pemformatan input seperti yang diterapkan di KnockoutJS misalnya).
Tyblitz

53

Pertanyaannya adalah, mengapa kekekalan begitu penting? Apa yang salah dalam bermutasi objek? Bukankah itu membuat hal-hal sederhana?

Sebenarnya, yang terjadi adalah kebalikannya: ketidakstabilan membuat segalanya menjadi lebih rumit, setidaknya dalam jangka panjang. Ya, itu membuat pengkodean awal Anda lebih mudah karena Anda hanya dapat mengubah hal-hal di mana pun Anda inginkan, tetapi ketika program Anda menjadi lebih besar itu menjadi masalah - jika suatu nilai berubah, apa yang mengubahnya?

Saat Anda membuat semuanya tidak berubah, itu berarti data tidak dapat diubah lagi secara mengejutkan. Anda tahu pasti bahwa jika Anda meneruskan nilai ke suatu fungsi, itu tidak dapat diubah dalam fungsi itu.

Sederhananya: jika Anda menggunakan nilai-nilai yang tidak dapat diubah, itu membuatnya sangat mudah untuk alasan tentang kode Anda: semua orang mendapatkan salinan * data Anda yang unik, sehingga tidak bisa hilang dengan itu dan merusak bagian lain dari kode Anda. Bayangkan betapa mudahnya ini membuat bekerja di lingkungan multi-utas!

Catatan 1: Ada biaya kinerja potensial untuk ketidakmampuan tergantung pada apa yang Anda lakukan, tetapi hal-hal seperti Immutable.js mengoptimalkan sebaik mungkin.

Catatan 2: Jika Anda tidak yakin, Immutable.js dan ES6 const arti yang sangat berbeda.

Dalam kasus biasa, saya bisa saja menambahkan objek ke array. Bagaimana saya mencapai dalam kasus ini? Hapus toko & buat ulang itu? Bukankah menambahkan objek ke array operasi yang lebih murah? PS: Jika contohnya bukan cara yang tepat untuk menjelaskan ketidakberdayaan, tolong beri tahu saya apa contoh praktis yang benar.

Ya, contoh berita Anda sangat bagus, dan alasan Anda tepat: Anda tidak bisa hanya mengubah daftar yang ada, jadi Anda perlu membuat yang baru:

var originalItems = Immutable.List.of(1, 2, 3);
var newItems = originalItems.push(4, 5, 6);

1
Saya tidak setuju dengan jawaban ini tetapi itu tidak menjawab bagian "Saya ingin belajar dari contoh praktis" dari pertanyaan. Orang bisa berargumen bahwa referensi tunggal ke daftar tajuk berita yang digunakan di banyak bidang adalah hal yang baik. "Saya hanya perlu memperbarui daftar sekali dan segala sesuatu yang merujuk pada daftar berita diperbarui secara gratis" - Saya pikir jawaban yang lebih baik akan mengambil masalah umum seperti yang disajikannya, dan menunjukkan alternatif berharga yang menggunakan ketetapan.
Terima kasih

1
Saya senang jawabannya sangat membantu! Mengenai pertanyaan baru Anda: jangan coba tebak sistem :) Dalam kasus yang tepat ini, sesuatu yang disebut "berbagi struktural" mengurangi GC meronta-ronta secara dramatis - jika Anda memiliki 10.000 item dalam daftar dan menambahkan 10 lebih banyak, saya percaya Immutable. js akan mencoba menggunakan kembali struktur sebelumnya sebaik mungkin. Biarkan Immutable.js khawatir tentang memori dan kemungkinan Anda akan menemukan itu keluar lebih baik.
TwoStraws

6
Imagine how much easier this makes working in a multi-threaded environment!-> Ok untuk bahasa lain tetapi ini bukan keuntungan dalam JavaScript single-threaded.
Steven de Salas

1
@StevendeSalas perhatikan bahwa JavaScript pada dasarnya tidak sinkron dan dikendalikan oleh peristiwa. Sama sekali tidak kebal terhadap kondisi ras.
Jared Smith

1
@ JaredSmith masih poin saya tetap. FP dan Immutability adalah paradigma yang sangat berguna untuk menghindari korupsi data dan / atau kunci sumber daya di lingkungan multithreaded tetapi tidak demikian dalam JavaScript karena single threaded. Kecuali saya kehilangan beberapa nugget suci kebijaksanaan, pertukaran utama di sini adalah apakah Anda siap untuk membuat Anda kode lebih kompleks (dan lebih lambat) dalam upaya untuk menghindari kondisi ras ... yang jauh lebih sedikit masalah daripada kebanyakan orang berpikir.
Steven de Salas

37

Meskipun jawaban lain baik-baik saja, untuk menjawab pertanyaan Anda tentang kasus penggunaan praktis (dari komentar di jawaban lain) mari kita melangkah keluar dari kode berjalan Anda sebentar dan melihat jawaban di mana-mana tepat di bawah hidung Anda: git . Apa yang akan terjadi jika setiap kali Anda mendorong komit Anda menimpa data dalam repositori?

Sekarang kita masuk ke salah satu masalah yang dihadapi koleksi abadi: memory bloat. Git cukup pintar untuk tidak hanya membuat salinan file baru setiap kali Anda membuat perubahan, itu hanya melacak diffs .

Meskipun saya tidak tahu banyak tentang cara kerja internal git, saya hanya bisa berasumsi itu menggunakan strategi yang mirip dengan perpustakaan yang Anda referensi: berbagi struktural. Di bawah tenda yang digunakan perpustakaan mencoba atau pohon lain untuk hanya melacak node yang berbeda.

Strategi ini juga cukup berkinerja untuk struktur data dalam memori karena ada algoritma operasi pohon terkenal yang beroperasi dalam waktu logaritmik.

Kasus penggunaan lain: misalkan Anda ingin membatalkan tombol pada aplikasi web Anda. Dengan representasi data Anda yang tidak berubah, penerapannya relatif sepele. Tetapi jika Anda mengandalkan mutasi, itu berarti Anda harus khawatir tentang caching keadaan dunia dan membuat pembaruan atom.

Singkatnya, ada harga yang harus dibayar untuk kekekalan dalam kinerja runtime dan kurva pembelajaran. Tetapi setiap programmer yang berpengalaman akan memberi tahu Anda bahwa waktu debug melebihi waktu penulisan kode dengan urutan besarnya. Dan sedikit hit pada kinerja runtime kemungkinan lebih besar daripada bug yang berhubungan dengan negara Anda tidak harus bertahan.


1
Sebuah contoh cemerlang yang saya katakan. Pemahaman saya tentang ketidakberdayaan lebih jelas sekarang. Terima kasih Jared. Sebenarnya, salah satu implementasinya adalah tombol UNDO: D Dan Anda membuat semuanya menjadi sangat sederhana bagi saya.
bozzmob

3
Hanya karena sebuah pola masuk akal pada git tidak berarti hal yang sama masuk akal di mana-mana. Di git Anda benar-benar peduli dengan semua histori yang disimpan dan Anda ingin dapat menggabungkan berbagai cabang. Di frontend Anda tidak peduli tentang sebagian besar sejarah negara dan Anda tidak perlu semua kompleksitas ini.
Ski

2
@Ski itu hanya kompleks karena itu bukan default. Saya biasanya tidak menggunakan mori atau immutable.js dalam proyek-proyek saya: Saya selalu ragu untuk mengambil deps pihak ketiga. Tetapi jika itu adalah default (a la clojurescript) atau setidaknya memiliki opsi asli opt-in, saya akan menggunakannya sepanjang waktu, karena ketika saya misalnya program di clojure saya tidak segera memasukkan semuanya ke dalam atom.
Jared Smith

Joe Armstrong akan berkata jangan khawatir tentang kinerja, tunggu saja beberapa tahun dan hukum Moore akan mengurusnya untuk Anda.
ximo

1
@JaredSmith Anda benar, segalanya semakin kecil dan sumber daya semakin terbatas. Saya tidak yakin apakah itu akan menjadi faktor pembatas untuk JavaScript. Kami terus mencari cara baru untuk meningkatkan kinerja (Svelte misalnya). Omong-omong, saya sepenuhnya setuju dengan komentar Anda yang lain. Kompleksitas atau kesulitan menggunakan struktur data yang tidak dapat diubah sering kali disebabkan oleh bahasa yang tidak memiliki dukungan bawaan untuk konsep tersebut. Clojure membuat kekekalan sederhana karena dimasukkan ke dalam bahasa, seluruh bahasa dirancang di sekitar ide.
ximo

8

Pertanyaannya adalah, mengapa kekekalan begitu penting? Apa yang salah dalam bermutasi objek? Bukankah itu membuat hal-hal sederhana?

Tentang mutabilitas

Tidak ada yang salah dalam mutabilitas dari sudut pandang teknis. Cepat, menggunakan kembali memori. Pengembang sudah terbiasa sejak awal (seingat saya). Masalah ada dalam penggunaan sifat berubah-ubah dan masalah yang dapat membawa penggunaan ini.

Jika objek tidak dibagi dengan apa pun, misalnya ada dalam lingkup fungsi dan tidak terpapar ke luar, maka sulit untuk melihat manfaat dalam keabadian. Sungguh dalam hal ini tidak masuk akal untuk berubah. Rasa kekekalan dimulai ketika sesuatu dibagikan.

Sakit kepala yang berubah-ubah

Struktur bersama yang dapat berubah dapat dengan mudah membuat banyak jebakan. Setiap perubahan di bagian mana pun dari kode dengan akses ke referensi berdampak ke bagian lain dengan visibilitas referensi ini. Dampak seperti itu menghubungkan semua bagian bersama-sama, bahkan ketika mereka seharusnya tidak menyadari modul yang berbeda. Mutasi dalam satu fungsi dapat merusak bagian aplikasi yang sama sekali berbeda. Hal seperti itu merupakan efek samping yang buruk.

Selanjutnya seringkali masalah dengan mutasi adalah keadaan rusak. Keadaan rusak dapat terjadi ketika prosedur mutasi gagal di tengah, dan beberapa bidang telah dimodifikasi dan beberapa tidak.

Terlebih lagi, dengan mutasi sulit untuk melacak perubahan. Pemeriksaan referensi sederhana tidak akan menunjukkan perbedaan, untuk mengetahui apa yang mengubah beberapa pemeriksaan mendalam perlu dilakukan. Juga untuk memantau perubahan, beberapa pola yang dapat diamati perlu diperkenalkan.

Akhirnya, mutasi adalah alasan defisit kepercayaan. Bagaimana Anda bisa yakin bahwa beberapa struktur memiliki nilai yang diinginkan, jika dapat dimutasi.

const car = { brand: 'Ferrari' };
doSomething(car);
console.log(car); // { brand: 'Fiat' }

Seperti yang ditunjukkan contoh di atas, melewati struktur yang dapat berubah selalu dapat diselesaikan dengan memiliki struktur yang berbeda. Fungsi doSomething adalah memutasi atribut yang diberikan dari luar. Tidak percaya dengan kode, Anda benar-benar tidak tahu apa yang Anda miliki dan apa yang akan Anda miliki. Semua masalah ini terjadi karena: Struktur yang dapat berubah menunjukkan pointer ke memori.

Kekekalan adalah tentang nilai

Kekekalan berarti bahwa perubahan tidak dilakukan pada objek, struktur yang sama, tetapi perubahan diwakili dalam yang baru. Dan ini karena referensi mewakili nilai bukan hanya penunjuk memori. Setiap perubahan menciptakan nilai baru dan tidak menyentuh yang lama. Aturan yang jelas seperti itu mengembalikan kepercayaan dan prediktabilitas kode. Fungsi aman untuk digunakan karena alih-alih mutasi, mereka berurusan dengan versi sendiri dengan nilai sendiri.

Menggunakan nilai alih-alih wadah memori memberikan kepastian bahwa setiap objek mewakili nilai spesifik yang tidak dapat diubah dan aman untuk menggunakannya.

Struktur yang tidak dapat berubah mewakili nilai.

Saya menyelam lebih dalam ke subjek dalam artikel sedang - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310


6

Mengapa imutabilitas sangat penting (atau dibutuhkan) dalam JavaScript?

Kekekalan dapat dilacak dalam konteks yang berbeda, tetapi yang paling penting adalah untuk melacaknya terhadap kondisi aplikasi dan terhadap UI aplikasi.

Saya akan mempertimbangkan pola JavaScript Redux sebagai pendekatan yang sangat trendi dan modern dan karena Anda menyebutkannya.

Untuk UI kita harus membuatnya dapat diprediksi . Akan dapat diprediksi jika UI = f(application state).

Aplikasi (dalam JavaScript) mengubah keadaan melalui tindakan yang diimplementasikan menggunakan fungsi peredam .

Fungsi peredam hanya mengambil tindakan dan kondisi lama dan mengembalikan kondisi baru, menjaga kondisi lama tetap utuh.

new state  = r(current state, action)

masukkan deskripsi gambar di sini

Manfaatnya adalah: Anda melakukan perjalanan waktu ke negara bagian karena semua objek keadaan disimpan, dan Anda dapat membuat aplikasi dalam keadaan apa pun sejak UI = f(state)

Jadi Anda dapat membatalkan / mengulang dengan mudah.


Terjadi untuk membuat semua keadaan ini masih bisa hemat memori, analogi dengan Git hebat, dan kami memiliki analogi serupa di OS Linux dengan tautan simbolis (berdasarkan pada inode).


5

Manfaat lain dari Ketidakmampuan dalam Javascript adalah mengurangi Temporal Coupling, yang memiliki manfaat besar untuk desain secara umum. Pertimbangkan antarmuka objek dengan dua metode:

class Foo {

      baz() {
          // .... 
      }

      bar() {
          // ....
      }

}

const f = new Foo();

Mungkin diperlukan suatu panggilan baz()untuk mendapatkan objek dalam kondisi yang valid agar panggilan bar()berfungsi dengan benar. Tapi bagaimana kamu tahu ini?

f.baz();
f.bar(); // this is ok

f.bar();
f.baz(); // this blows up

Untuk mengetahuinya, Anda perlu memeriksa internal kelas karena tidak segera terlihat dari memeriksa antarmuka publik. Masalah ini dapat meledak dalam basis kode besar dengan banyak status dan kelas yang bisa berubah.

Jika Footidak berubah maka ini tidak lagi menjadi masalah. Aman untuk mengasumsikan bahwa kita dapat menelepon bazatau bardalam urutan apa pun karena keadaan dalam kelas tidak dapat berubah.


4

Sekali waktu, ada masalah dengan sinkronisasi data antara utas. Masalah ini sangat menyakitkan, ada 10+ solusi. Beberapa orang mencoba menyelesaikannya secara radikal. Itu adalah tempat di mana pemrograman fungsional lahir. Seperti halnya Marxisme. Saya tidak mengerti bagaimana Dan Abramov menjual ide ini ke JS, karena itu adalah utas tunggal. Dia jenius.

Saya bisa memberi contoh kecil. Ada atribut __attribute__((pure))di gcc. Compiler mencoba memecahkan apakah fungsi Anda murni atau tidak jika Anda tidak akan mendeklarasikannya secara khusus. Fungsi Anda bisa murni bahkan negara Anda bisa berubah. Kekekalan hanyalah salah satu dari 100 cara untuk menjamin bahwa fungsi Anda akan murni. Sebenarnya 95% dari fungsi Anda akan menjadi murni.

Anda tidak boleh menggunakan batasan apa pun (seperti kekekalan) jika Anda sebenarnya tidak memiliki alasan serius. Jika Anda ingin "Membatalkan" suatu keadaan, Anda dapat membuat transaksi. Jika Anda ingin menyederhanakan komunikasi, Anda dapat mengirim acara dengan data yang tidak dapat diubah. Ini terserah kamu.

Saya menulis pesan ini dari republik post marxism. Saya yakin bahwa radikalisasi ide apa pun adalah cara yang salah.


Paragraf ketiga sangat masuk akal. Terima kasih untuk itu. 'Jika Anda ingin "Membatalkan" suatu keadaan, Anda dapat membuat transaksi' !!
bozzmob

Omong-omong, perbandingan dengan Marxisme juga bisa dibuat untuk OOP. Ingat Java? Heck, bit aneh Jawa dalam JavaScript? Hype tidak pernah baik, itu menyebabkan radikalisasi dan polarisasi. Secara historis, OOP jauh lebih menggembirakan dibandingkan dengan Facebook tentang Redux. Meskipun mereka benar-benar mencoba yang terbaik.
ximo

4

Pandangan Berbeda ...

Jawaban saya yang lain menjawab pertanyaan dari sudut pandang yang sangat praktis, dan saya masih menyukainya. Saya telah memutuskan untuk menambahkan ini sebagai jawaban lain daripada addendum karena jawaban itu adalah kata-kata filosofis yang membosankan yang semoga juga menjawab pertanyaan, tetapi tidak benar-benar cocok dengan jawaban saya yang ada.

TL; DR

Bahkan dalam proyek-proyek kecil kekekalan dapat berguna, tetapi jangan berasumsi bahwa karena itu ada, itu dimaksudkan untuk Anda.

Jauh, jauh lebih lama jawabannya

CATATAN: untuk keperluan jawaban ini saya menggunakan kata 'disiplin' untuk menyangkal diri demi keuntungan.

Ini serupa dalam bentuk dengan pertanyaan lain: "Haruskah saya menggunakan naskah? Mengapa jenis begitu penting dalam JavaScript?". Ini memiliki jawaban yang serupa juga. Pertimbangkan skenario berikut:

Anda adalah satu-satunya penulis dan pemelihara basis kode JavaScript / CSS / HTML dari sekitar 5000 baris. Bos semi-teknis Anda membaca sesuatu tentang Typescript-as-the-new-hotness dan menyarankan bahwa kami mungkin ingin pindah ke sana tetapi menyerahkan keputusan kepada Anda. Jadi Anda membacanya, bermain dengannya, dll.

Jadi sekarang Anda punya pilihan untuk dibuat, apakah Anda pindah ke Filescript?

Mengetik naskah memiliki beberapa keuntungan menarik: intellisense, menangkap kesalahan lebih awal, menentukan API Anda di muka, kemudahan memperbaiki hal-hal ketika refactoring memecahkannya, lebih sedikit tes. Jenis naskah juga memiliki beberapa biaya: idiom JavaScript tertentu yang sangat alami dan benar dapat menjadi model yang rumit dalam sistem jenisnya yang tidak terlalu kuat, anotasi menumbuhkan LoC, waktu dan upaya penulisan ulang basis kode yang ada, langkah tambahan dalam membangun pipa, dll. Lebih mendasar lagi, ini mengukir sebagian dari kemungkinan program JavaScript yang benar dengan imbalan janji bahwa kode Anda lebih mungkin benar. Ini bersifat membatasi semena-mena. Itulah intinya: Anda memaksakan disiplin yang membatasi Anda (semoga dari menembak diri sendiri di kaki).

Kembali ke pertanyaan, diulang dalam konteks paragraf di atas: apakah itu layak ?

Dalam skenario yang dijelaskan, saya akan berpendapat bahwa jika Anda sangat akrab dengan basis kode JS kecil-ke-menengah, bahwa pilihan untuk menggunakan Scriptex lebih estetika daripada praktis. Dan itu baik - baik saja , tidak ada yang salah dengan estetika, mereka hanya belum tentu menarik.

Skenario B:

Anda berganti pekerjaan dan sekarang menjadi programmer bidang usaha di Foo Corp. Anda bekerja dengan tim 10 pada 90000 LoC (dan terus bertambah) JavaScript / HTML / CSS basis kode dengan pipeline membangun yang cukup rumit yang melibatkan babel, webpack , seperangkat polyfill, bereaksi dengan berbagai plugin, sistem manajemen negara, ~ 20 perpustakaan pihak ketiga, ~ 10 perpustakaan internal, plugin editor seperti linter dengan aturan untuk panduan gaya in-house, dll.

Kembali ketika Anda 5k pria / wanita LoC, itu tidak masalah. Dokumentasi bahkan tidak bahwa masalah besar, bahkan kembali ke bagian tertentu dari kode setelah 6 bulan Anda bisa mencari tahu dengan mudah cukup. Tetapi sekarang disiplin bukan hanya baik tetapi perlu . Disiplin itu mungkin tidak melibatkan naskah, tetapi kemungkinan akan melibatkan beberapa bentuk analisis statis serta semua bentuk disiplin pengkodean lainnya (dokumentasi, panduan gaya, skrip pembuatan, pengujian regresi, CI). Disiplin bukan lagi suatu kemewahan , melainkan suatu keharusan .

Semua ini diterapkan GOTOpada tahun 1978: permainan blackjack kecil mungil Anda di C dapat menggunakan GOTOlogika s dan spageti dan itu bukan masalah besar untuk memilih petualangan Anda sendiri melewatinya, tetapi ketika program semakin besar dan lebih ambisius, baik, disiplin penggunaan GOTOtidak bisa dipertahankan. Dan semua ini berlaku untuk kekekalan hari ini.

Sama seperti tipe statis, jika Anda tidak bekerja pada basis kode besar dengan tim insinyur mempertahankan / memperluasnya, pilihan untuk menggunakan immutability lebih estetika daripada praktis: manfaatnya masih ada tetapi mungkin belum melebihi biayanya.

Tetapi seperti halnya dengan semua disiplin ilmu yang bermanfaat, ada satu titik di mana ia tidak lagi opsional. Jika saya ingin mempertahankan berat badan yang sehat, maka disiplin yang melibatkan es krim mungkin opsional. Tetapi jika saya ingin menjadi atlet yang kompetitif, pilihan saya untuk makan es krim atau tidak dimasukkan oleh tujuan pilihan saya. Jika Anda ingin mengubah dunia dengan perangkat lunak, ketidakmampuan mungkin menjadi bagian dari apa yang Anda butuhkan untuk menghindarinya agar tidak runtuh karena beratnya sendiri.


1
+1 Saya menyukainya. Lebih banyak tentang titik Jared. Namun kekekalan tidak akan menyelamatkan tim dari kurangnya disiplinnya sendiri. 😉
Steven de Salas

@StevendeSalas adalah bentuk disiplin. Dan dengan demikian, saya pikir itu berkorelasi dengan (tetapi tidak menggantikan) bentuk-bentuk lain dari disiplin rekayasa perangkat lunak. Itu melengkapi, bukannya menggantikan. Tetapi seperti yang saya katakan dalam komentar atas jawaban Anda, saya tidak terkejut sama sekali bahwa itu didorong oleh raksasa teknologi dengan sekelompok insinyur yang semuanya bekerja pada basis kode besar yang sama :) mereka membutuhkan semua disiplin yang bisa mereka dapatkan. Saya sebagian besar tidak bermutasi objek tetapi juga tidak menggunakan segala bentuk penegakan karena, hanya saya.
Jared Smith

0

Saya telah membuat kerangka kerja agnostik open source (MIT) lib untuk keadaan berubah-ubah (atau tidak berubah) yang dapat menggantikan semua penyimpanan yang tidak dapat diubah seperti libs (redux, vuex dll ...).

Keadaan yang tidak dapat diubah jelek bagi saya karena terlalu banyak pekerjaan yang harus dilakukan (banyak tindakan untuk operasi baca / tulis sederhana), kode kurang dapat dibaca dan kinerja untuk dataset besar tidak dapat diterima (seluruh komponen dibuat ulang: /).

Dengan deep-state-observer, saya hanya dapat memperbarui satu node dengan notasi titik dan menggunakan wildcard. Saya juga dapat membuat riwayat status (undo / redo / time travel) dengan mempertahankan nilai konkret yang telah diubah {path:value}= penggunaan memori yang lebih sedikit.

Dengan deep-state-observer, saya dapat menyempurnakan hal-hal dan saya memiliki kendali butir atas perilaku komponen sehingga kinerja dapat ditingkatkan secara drastis. Kode lebih mudah dibaca dan refactoring jauh lebih mudah - cari saja dan ganti string path (tidak perlu mengubah kode / logika).


-1

Saya pikir alasan utama objek tidak berubah, adalah menjaga status objek tetap valid.

Misalkan kita memiliki objek yang disebut arr. Objek ini valid ketika semua item adalah huruf yang sama.

// this function will change the letter in all the array
function fillWithZ(arr) {
    for (var i = 0; i < arr.length; ++i) {
        if (i === 4) // rare condition
            return arr; // some error here

        arr[i] = "Z";
    }

    return arr;
}

console.log(fillWithZ(["A","A","A"])) // ok, valid state
console.log(fillWithZ(["A","A","A","A","A","A"])) // bad, invalid state

jika arrmenjadi objek yang tidak dapat diubah, maka kita akan yakin bahwa arr selalu dalam keadaan valid.


Saya pikir arrakan dimutasi setiap kali Anda meneleponfillWithZ
rodrigo-silveira

jika Anda menggunakan immutable.js, Anda akan mendapatkan salinan objek baru setiap kali Anda mengubahnya. jadi objek aslinya tidak tersentuh
bedorlan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.