JavaScript'te değişmezlik neden bu kadar önemli (veya gerekli)?


207

Şu anda React JS ve React Native çerçeveleri üzerinde çalışıyorum . Yarım yolda Facebook'un Flux ve Redux uygulaması hakkında okurken Immutability veya Immutable-JS kütüphanesine rastladım .

Soru şu: Değişmezlik neden bu kadar önemli? Nesneleri değiştirirken yanlış olan ne? İşleri basitleştirmiyor mu?

Bir örnek vermek gerekirse, açılış ekranı haber başlıklarının bir liste görünümü olan basit bir Haber okuyucu uygulamasını ele alalım .

Ayarladıysam değeri olan bir nesne dizisi söyleBaşlangıçta , onu değiştiremem. Değişmezlik ilkesi bunu söylüyor, değil mi? (Yanlışsam beni düzeltin.) Ama, güncellenmesi gereken yeni bir Haber nesnesim varsa ne olur? Her zamanki gibi, nesneyi diziye ekleyebilirdim. Bu durumda nasıl başarabilirim? Mağazayı silmek ve yeniden oluşturmak? Diziye bir nesne eklemek daha ucuz bir işlem değil midir?



2
Değişmez veri yapısı ve saf işlev referans şeffaflığına yol açarak programınızın davranışı hakkında akıl yürütmeyi çok daha kolay hale getirir. Ayrıca fonksiyonel veri yapısını kullanırken ücretsiz geri izleme de alabilirsiniz.
WorBlux

Bir Redux bakış açısı @bozzmob sağladım.
prosti

1
JS'nin bununla ilgili bir şeyleri olduğunu düşünmeye çalışmak yerine işlevsel bir paradigma kavramı olarak genel olarak ölçülemezliği öğrenmek yararlı olabilir. Reaksiyon, fonksiyonel programlama hayranları tarafından yazılmıştır. Onları anlamak için bildiklerini bilmek zorundasınız.
Gherman

Gerekli değil, ama bazı hoş ticari teklifler sunuyor. Hareketli Parçalar Donanım Olarak Mutable State Yazılımdır
Kristian Dupont

Yanıtlar:


198

Son zamanlarda aynı konuyu araştırıyorum. Sorularınızı cevaplamak ve şimdiye kadar öğrendiklerimi paylaşmak için elimden geleni yapacağım.

Soru şu: Değişmezlik neden bu kadar önemli? Nesneleri değiştirirken yanlış olan ne? İşleri basitleştirmiyor mu?

Temel olarak, değişmezliğin öngörülebilirliği, performansı (dolaylı olarak) artırdığı ve mutasyon izlemeye izin verdiği gerçeği ortaya çıkar.

öngörülebilirlik

Mutasyon, kötü hatalara neden olabilecek (beklenmeyen) yan etkiler yaratan değişimi gizler. Değişmezliği uyguladığınızda, uygulama mimarinizi ve zihinsel modelinizi basit tutabilirsiniz, bu da uygulamanızla ilgili akıl yürütmeyi kolaylaştırır.

Verim

Değişken bir Nesneye değer eklemek, mevcut değerlerin kopyalanması gereken yeni bir örneğin oluşturulması gerektiği ve belleğe mal olan yeni Nesneye yeni değerlerin eklenmesi gerektiği anlamına gelse de, değişmez Nesneler belleği azaltmak için yapısal paylaşımı kullanabilir havai.

Tüm güncellemeler yeni değerler döndürür, ancak bellek kullanımını (ve GC'yi azaltmayı) önemli ölçüde azaltmak için dahili olarak yapılar paylaşılır. Bu, 1000 öğeli bir vektöre eklerseniz, aslında 1001 element uzunluğunda yeni bir vektör oluşturmayacağı anlamına gelir. Büyük olasılıkla, dahili olarak sadece birkaç küçük nesne tahsis edilir.

Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz .

Mutasyon Takibi

Azaltılmış bellek kullanımının yanı sıra, değişmezlik, referans ve değer eşitliğini kullanarak uygulamanızı optimize etmenizi sağlar. Bu, bir şeyin değişip değişmediğini görmeyi gerçekten kolaylaştırır. Örneğin, bir reaksiyon bileşenindeki durum değişikliği. Sen kullanabilirsiniz shouldComponentUpdatedevlet karşılaştırarak devlet Nesneler tarafından aynıdır ve gereksiz oluşturma önlemek olmadığını kontrol etmek. Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz .

Ek kaynaklar:

Ayarladıysam başlangıçta değeri olan bir nesne dizisi söylerim. Ben manipüle edemem. Değişmezlik ilkesi bunu söylüyor, değil mi? (Yanlışsam beni düzeltin). Ancak, güncellenmesi gereken yeni bir Haber nesnesim varsa ne olur? Her zamanki gibi, nesneyi diziye ekleyebilirdim. Bu durumda nasıl başarabilirim? Mağaza silinsin mi ve yeniden oluşturulsun mu? Diziye bir nesne eklemek daha ucuz bir işlem değil midir?

Evet bu doğru. Bunu uygulamanızda nasıl uygulayacağınız konusunda kafanız karıştıysa , redux'un temel kavramları tanımak için bunu nasıl yaptığına bakmanızı tavsiye ederim , bana çok yardımcı oldu.

Redux'u örnek olarak kullanmayı seviyorum çünkü değişmezliği kucaklıyor. storeTüm durum değişikliklerinin, adı geçen eylemlerle birlikte önceki durumu kabul eden (her seferinde bir tane) bir redüktör tarafından işlenen eylemleri göndererek ve uygulamanızın bir sonraki durumunu döndüren tek bir değişmez durum ağacına (olarak adlandırılır ) sahiptir. . Temel ilkeleri hakkında daha fazla bilgiyi buradan edinebilirsiniz .

Egghead.io'da mükemmel bir redux kursu var, burada redux yazarı Dan Abramov bu ilkeleri şu şekilde açıklıyor (senaryoya daha iyi uyacak şekilde kodu biraz değiştirdim):

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();

Ayrıca, bu videolar aşağıdakiler için değişmezliğe nasıl ulaşılacağını daha ayrıntılı olarak göstermektedir:


1
@ naomik geri bildiriminiz için teşekkürler! Niyetim, konsepti göstermek ve Nesnelerin mutasyona uğratılmadığını açıkça göstermek ve onu tamamen nasıl uygulayacağımızı göstermek değildi. Ancak, örneğim biraz kafa karıştırıcı olabilir, biraz güncelleyeceğim.
danillouz

2
@bozzmob rica ederim! Hayır, bu doğru değil, redüktördeki değişmezliği kendiniz uygulamanız gerekir. Bu, videolarda gösterildiği gibi stratejileri takip edebileceğiniz veya immutablejs gibi bir kütüphane kullanabileceğiniz anlamına gelir. Burada ve burada daha fazla bilgi bulabilirsiniz .
danillouz

1
@naomik ES6 constdeğişmezlik hakkında değildir. Mathias Bynens bu konuda harika bir blog makalesi yazdı .
Lea Rosema

1
@terabaud Bağlantıyı paylaştığınız için teşekkürler. Bunun önemli bir ayrım olduğuna katılıyorum. ^ _ ^
Teşekkürler

4
Lütfen açıklayın "Mutasyon kötü hatalara neden olabilecek (beklenmeyen) yan etkiler yaratan değişikliği gizler. Değişmezliği zorladığınızda uygulama mimarinizi ve zihinsel modelinizi basit tutabilirsiniz, bu da uygulamanızla ilgili akıl yürütmenizi kolaylaştırır." Çünkü bu JavaScript bağlamında hiç doğru değil.
Pavle Lekic

144

Değişmezliğe Karşıt Bir Bakış

TL / DR: Değişmezlik, JavaScript'te bir zorunluluktan çok bir moda trendi. React kullanıyorsanız , devlet yönetimindeki bazı kafa karıştırıcı tasarım seçeneklerine düzgün bir çözüm sağlar . Bununla birlikte, diğer birçok durumda, getirdiği karmaşıklığa yeterince değer katmayacak ve bir özgeçmiş doldurmak için daha fazla hizmet verecektir gerçek bir müşteri ihtiyacını karşılamaktan daha fazla .

Uzun cevap: aşağıyı okuyun.

Javascript'te değişmezlik neden bu kadar önemli (veya gerekli)?

Sorduğunuza sevindim!

Bir süre önce Dan Abramov adında çok yetenekli bir adam , Redux adlı saf fonksiyonları ve değişmezliği kullanan bir javascript devlet yönetimi kütüphanesi yazdı . Ayrıca , fikri gerçekten anlaşılmasını (ve satmasını) kolaylaştıran bazı harika videolar da yaptı.

Zamanlama mükemmeldi. Yeniliği açısal solma edildi ve JavaScript Dünya serin sağ derecesini vardı son şey sabitleşmek hazırdı ve bu kütüphane sadece yenilikçi değil ama mükemmel oluklu React diğeri tarafından sokak sokak hangi Silikon Vadisi güç merkezi .

Ne yazık ki, JavaScript dünyasında moda hakimdir. Şimdi Abramov yarı tanrı olarak selamlanıyor ve hepimiz sadece ölümlüler kendimizi Değişmezlik Dao'una tabi tutmalıyız ... Mantıklı olsun ya da olmasın.

Nesneleri değiştirirken yanlış olan ne?

Hiçbir şey değil!

Aslında, programcılar mutasyona uğrayan nesneler olduğu sürece nesneleri eritiriyorlar. 50 yıldan fazlaYani fazla uygulama geliştirme.

Ve neden işleri karmaşıklaştırıyor? Nesneniz olduğunda catve öldüğünde, catdeğişikliği izlemek için gerçekten bir saniyeye mi ihtiyacınız var ? Çoğu insan bunu söyler cat.isDead = trueve onunla yapılır.

(Nesneleri değiştirerek) işleri basitleştirmiyor mu?

EVET! .. Elbette öyle!

Özellikle JavaScript'te, pratikte başka bir yerde (veritabanında olduğu gibi) korunan bazı durumların görüntülenmesi için en kullanışlı olanıdır.

Güncellenmesi gereken yeni bir Haber nesnesim varsa ne olur? ... Bu durumda nasıl başarabilirim? Mağaza silinsin mi ve yeniden oluşturulsun mu? Diziye bir nesne eklemek daha ucuz bir işlem değil midir?

Geleneksel yaklaşıma gidebilir ve Newsnesneyi güncelleyebilirsiniz , böylece o nesnenin bellek içi gösterimi değişir (ve kullanıcıya görüntülenen görünüm, ya da biri umar) ...

Veya alternatif olarak...

Seksi FP / Immutability yaklaşımını deneyebilir ve her tarihsel değişikliği izleyen bir diziye değişikliklerinizi Newsnesneye ekleyebilirsiniz, böylece dizi üzerinden yineleyebilir ve doğru durum gösteriminin ne olması gerektiğini anlayabilirsiniz (vay!).

Burada ne olduğunu öğrenmeye çalışıyorum. Lütfen beni aydınlatın :)

Moda gelir ve gider dostum. Bir kedinin derisini almanın birçok yolu vardır.

Sürekli değişen bir dizi programlama paradigmasının karışıklığına katlanmak zorunda olduğunuz için üzgünüm. Ama hey, KULÜBE HOŞGELDİNİZ !!

Şimdi, Değişmezlik ile ilgili hatırlanması gereken birkaç önemli nokta var ve bunları sadece naifliğin toplayabileceği ateşli yoğunluk ile atacaksınız.

1) Değişmezlik, çok iş parçacıklı ortamlarda yarış koşullarından kaçınmak için harika .

Çok iş parçacıklı ortamlar (C ++, Java ve C # gibi), birden fazla iş parçacığı bunları değiştirmek istediğinde nesneleri kilitleme uygulamasından suçludur. Bu performans için kötü, ancak veri bozulması alternatifinden daha iyidir. Ve yine de her şeyi değişmez yapmak kadar iyi değil (Lord Haskell'i övüyor!).

AMA ALAS! JavaScript'te her zaman tek bir iş parçacığında çalışırsınız . Web çalışanları bile (her biri ayrı bir bağlam içinde çalışır ). Dolayısıyla , yürütme bağlamınızda (tüm bu güzel küresel değişkenler ve kapanışlar) iş parçacığıyla ilgili bir yarış koşuluna sahip olamayacağınız için, Değişmezlik lehine ana nokta pencereden dışarı çıkar.

(Orada, söyledikten olan ana iş parçacığı üzerinde nesneler ile işe yaramaz hakkında hiç beklentilere sahip olacak olmasıdır web işçileri, saf işlevlerini kullanarak bir avantajdır.)

2) Değişmezlik, bir şekilde uygulamanızın durumundaki yarış koşullarından kaçınabilir.

Ve işte konunun asıl noktası, çoğu (React) geliştiricisi, Immutability ve FP'nin bir şekilde başvurunuzun durumunun öngörülebilir olmasını sağlayan bu büyüyü çalıştırabileceğini söyleyecektir.

Tabii ki bu , veritabanındaki yarış koşullarından kaçınabileceğiniz anlamına gelmez, bunu kaldırmak için tüm tarayıcılardaki tüm kullanıcıları koordine etmeniz gerekir ve bunun için WebSockets ( bununla ilgili daha fazla bilgi), uygulamayı çalıştıran herkese değişiklikler yayınlayacak.

Ayrıca JavaScript'te, uygulama durumunuzun öngörülebilir olması için değişmezliğe ihtiyaç duyduğu bazı doğal bir sorun olduğu anlamına gelmez, React size bunu bildirmeden önce ön uç uygulamaları kodlayan herhangi bir geliştirici.

Bu oldukça kafa karıştırıcı iddia , React ile başvuru durumunuzun yarış koşullarına daha yatkın olacağı anlamına gelir , ancak bu değişmezlik bu acıyı ortadan kaldırmanıza izin verir. Neden? React özel olduğu için , devlet yönetimi neredeyse sonradan düşünülen son derece optimize edilmiş bir görüntüleme kütüphanesi olarak tasarlanmıştır ve bu nedenle bileşen durumu, kontrolünüz olmayan eşzamansız bir olaylar zinciri (diğer bir deyişle "tek yönlü veri bağlama") aracılığıyla yönetilir ve durumu doğrudan değiştirmemeyi hatırlayarak sana güveniyorum ...

Bu bağlam göz önüne alındığında, değişmezlik ihtiyacının JavaScript ile nasıl bir ilgisi olmadığını ve React'teki yarış koşullarıyla ne ilgisi olduğunu görmek kolaydır: eğer uygulamanızda bir dizi birbirine bağlı değişiklik varsa ve ne olduğunu anlamanın kolay bir yolu yoksa devletiniz şu anda karışıyor , kafanız karışacak ve bu nedenle her tarihsel değişikliği izlemek için değişmezliği kullanmak mantıklı .

3) Yarış koşulları kategorik olarak kötüdür.

React kullanıyorsanız bunlar olabilir. Ancak farklı bir çerçeve seçerseniz bunlar nadirdir.

Ayrıca, normalde başa çıkmak için çok daha büyük sorunlarınız var… Bağımlılık cehennemi gibi sorunlar. Şişirilmiş bir kod tabanı gibi. CSS'niz yüklenmiyor gibi. Yavaş bir inşa süreci veya yinelemeyi neredeyse imkansız kılan monolitik bir arka uca yapışmış gibi. Deneyimsiz geliştiriciler gibi neler olup bittiğini anlamıyor ve bir şeyleri karıştırıyor gibi.

Bilirsin. Gerçeklik. Ama hey, kimin umrunda?

4) Değişmezlik, her durum değişikliğinin izlenmesinin performans etkisini azaltmak için Referans Tiplerini kullanır.

Cidden, eğer durumunuz her değiştiğinde bir şeyler kopyalayacaksanız, bu konuda akıllı olduğunuzdan emin olmalısınız.

5) Değiştirilemez şeyler UNDO sağlar .

Çünkü er .. bu proje yöneticinizin isteyeceği bir numaralı özellik, değil mi?

6) Değişmez durum, WebSockets ile birlikte çok fazla serin potansiyele sahiptir

Son fakat en az değil, devlet deltalarının birikimi , değişmez olayların akışı olarak kolay bir devlet tüketimine izin veren WebSockets ile birlikte oldukça zorlayıcı bir durum ortaya çıkarıyor ...

Kuruş bu konsepte düştüğünde (devlet, bir olay akışıdır - en son görüşü temsil eden kaba bir kayıtlar grubu yerine), değişmez dünya yaşamak için büyülü bir yer haline gelir. Zamanın kendisini aşan olay kaynaklı bir merak ve olasılık ülkesi . Ve bittiğinde sağ bu kesinlikle gerçek zamanlı EASI uygulamalar yapabilir er gerçekleştirmek için, sadece onlar böylece ilgilenen herkese olayların akışını yayın kendi temsilini inşa mevcut ve ortak akışına kendi değişikliklerini geri yazın.

Fakat bir noktada uyanırsınız ve tüm bu mucizenin ve büyünün ücretsiz olmadığını fark edersiniz. İstekli meslektaşlarınızın aksine, paydaşlarınız (evet, size ödeme yapan kişiler) felsefeye veya modaya çok az önem veriyorlar ve satabilecekleri bir ürün oluşturmak için ödedikleri paraya çok önem veriyorlar. Sonuç olarak, değişmez kod yazmak daha zor ve kırılması daha kolay, ayrıca onu desteklemek için bir arka ucunuz yoksa değişmez bir ön uca sahip çok az nokta var. Son olarak, paydaşlarınızı WebSockets gibi bir itme teknolojisi aracılığıyla yayınlayıp tüketmeniz gerektiğine ikna ettiğinizde , üretimde ölçeklendirmenin ne kadar acı olduğunu anlarsınız .


Şimdi bazı tavsiyeler için, kabul etmeyi seçmelisiniz.

FP / Immutability kullanarak JavaScript yazma seçeneği, uygulama kod tabanınızı daha büyük, daha karmaşık ve yönetilmesi daha zor hale getirmek için de bir seçimdir. Ne yaptığınızı bilmiyorsanız, bu yaklaşımı Redux redüktörlerinizle sınırlandırmanızı şiddetle öneririm ... Ve eğer devam edip değişmezliği kullanacaksanız, o zaman sadece tüm uygulama yığınıza değişmez bir durum uygulayın , müşteri tarafında, aksi takdirde gerçek değerini kaçırdığınız için.

Şimdi, işinizde seçim yapabileceğiniz kadar şanslıysanız, bilgeliğinizi kullanmayı deneyin (ya da kullanmayın) ve size ödeme yapan kişinin doğru olanı yapın . Bunu deneyiminize, bağırsağınıza veya etrafınızda olup bitenlere dayandırabilirsiniz (kuşkusuz herkes React / Redux kullanıyorsa, çalışmaya devam etmek için bir kaynak bulmanın daha kolay olacağına dair geçerli bir argüman vardır) .. Alternatif olarak, Driven Development veya Hype Driven Development yaklaşımlarını deneyebilirsiniz . Onlar daha çok sizin türünüz olabilir.

Kısacası, değişmezlik için söylenecek olan şey , en azından bir sonraki çılgınlık ortaya çıkana kadar akranlarınızla modaya uygun hale gelecektir , bu noktada ilerlemekten memnuniyet duyacaksınız.


Şimdi bu kendi kendine terapi seansından sonra bunu blogumda bir makale olarak eklediğimi belirtmek isterim => JavaScript'te Değişmezlik: Aksi Görünüm . Göğsünüzden de kurtulmak istediğiniz güçlü duygularınız varsa, orada cevap vermekten çekinmeyin;).


10
Merhaba Steven, Evet. İmmutable.js ve redux'u düşündüğümde tüm bu şüphelerim vardı. Ama cevabınız inanılmaz! Çok değer katıyor ve şüphelerim olan her bir noktayı ele aldığınız için teşekkürler. Değişmez nesnelerde aylarca çalıştıktan sonra bile şimdi çok daha net / daha iyi.
bozzmob

5
Reax'i Flux / Redux ile iki yıldan fazla bir süredir kullanıyorum ve sizinle daha fazla anlaşamadım, harika yanıt!
Pavle Lekic

6
Değişmezlik hakkındaki görüşlerin takım ve kod tabanı boyutları ile oldukça düzgün bir şekilde ilişkili olduğundan şüpheleniyorum ve ana savunucunun silikon vadisi devi olması tesadüf değil. Bununla birlikte, kesinlikle katılmıyorum: değişmezlik, goto kullanmamak gibi yararlı bir disiplindir, yararlı bir disiplindir. Veya birim testi. Veya TDD. Veya statik tip analizi. Bunları her zaman, her seferinde yaptığınız anlamına gelmez (bazıları olsa da). Ayrıca, yararlılığın yutturmacaya dik olduğunu söyleyebilirim: yararlı / gereksiz ve seksi / sıkıcı bir matriste her birinin bolca örneği vardır. "hyped" !== "bad"
Jared Smith

4
Merhaba @ftor, iyi bir nokta, işleri diğer yöne çok ileri götürmek. Bununla birlikte, 'javascript'te değişmezlik' makaleleri ve argümanları gibi bir bolluk olduğu için, şeyleri dengelemem gerektiğini hissettim. Bu yüzden yeni başlayanların her iki şekilde de yargıda bulunmalarına yardımcı olmak için karşıt bir bakış açısı vardır.
Steven de Salas

4
Bilgilendirici ve zekice tasarlanmış. Bu cevabı bulana kadar benzer bir görüşe sahip tek kişi olduğumu sanıyordum. Değişmezliğin değerini anlıyorum, ama beni rahatsız eden şey, tüm diğer tekniği baskılayan bir dogma haline gelmesidir (örneğin, KnockoutJS'de uygulandığı gibi giriş biçimlendirmesi için delicesine faydalı olan 2 yönlü bağlamaya zarar vermek).
Tyblitz

53

Soru şu: Değişmezlik neden bu kadar önemli? Nesneleri değiştirirken yanlış olan ne? İşleri basitleştirmiyor mu?

Aslında, tam tersi geçerlidir: değişebilirlik, en azından uzun vadede işleri daha karmaşık hale getirir. Evet, ilk kodlama işleminizi kolaylaştırır çünkü işleri istediğiniz yerde değiştirebilirsiniz, ancak programınız büyüdüğünde sorun olur - bir değer değişirse, onu ne değiştirdi?

Her şeyi değişmez kıldığınızda, bu artık verilerin sürpriz bir şekilde değiştirilemeyeceği anlamına gelir. Bir işleve bir değer iletirseniz, o işlevde değiştirilemeyeceğini kesin olarak bilirsiniz.

Basitçe söylemek gerekirse: değişmez değerler kullanırsanız, kodunuz hakkında mantık yürütmenizi çok kolaylaştırır: herkes verilerinizin benzersiz bir * kopyasını alır, bu yüzden onunla gelemez ve kodunuzun diğer kısımlarını kıramaz. Bunun çok iş parçacıklı bir ortamda çalışmayı ne kadar kolay hale getirdiğini düşünün!

Not 1: Yaptığınız işe bağlı olarak değişmezliğin potansiyel bir performans maliyeti vardır, ancak Immutable.js gibi şeyler ellerinden gelenin en iyisini optimize eder.

Not 2: Olası bir olayda emin olamadınız, Immutable.js ve ES6 constçok farklı şeyler ifade ediyor.

Her zamanki gibi, nesneyi diziye ekleyebilirdim. Bu durumda nasıl başarabilirim? Mağaza silinsin mi ve yeniden oluşturulsun mu? Diziye bir nesne eklemek daha ucuz bir işlem değil midir? Not: Örnek değişmezliği açıklamanın doğru yolu değilse, lütfen doğru pratik örneğin ne olduğunu bana bildirin.

Evet, haber örneğiniz mükemmel derecede iyi ve muhakemeniz tam olarak doğru: sadece mevcut listenizi değiştiremezsiniz, bu yüzden yeni bir tane oluşturmanız gerekir:

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

1
Bu cevaba katılmıyorum ama sorunun "Pratik bir örnekten öğrenmek istiyorum" kısmına değinmiyor. Birden fazla alanda kullanılan haber başlıkları listesine yapılan tek bir referansın iyi bir şey olduğu söylenebilir. "Listeyi yalnızca bir kez güncellemeliyim ve haber listesine atıfta bulunan her şey ücretsiz olarak güncellenir" - Bence daha iyi bir yanıt, sunduğu gibi ortak bir problem alacak ve değişmezliği kullanan değerli bir alternatif gösterecektir.
Teşekkürler

1
Cevabın yardımcı olduğuna sevindim! Yeni sorunuzla ilgili olarak: sistemi tahmin etmeye çalışmayın :) Bu durumda, "yapısal paylaşım" adı verilen bir şey GC'nin çöp atmasını önemli ölçüde azaltır - bir listede 10.000 öğeniz varsa ve 10 tane daha eklerseniz, Immutable'a inanıyorum. js, önceki yapıyı mümkün olan en iyi şekilde kullanmaya çalışacaktır. Immutable.js'nin bellek hakkında endişelenmesine izin verin ve şansın daha iyi olduğunu fark edeceksiniz.
TwoStraws

6
Imagine how much easier this makes working in a multi-threaded environment!-> Diğer diller için tamam, ancak bu tek iş parçacıklı JavaScript'te bir avantaj değil.
Steven de Salas

1
@StevendeSalas, JavaScript'in öncelikle eşzamansız ve etkinlik odaklı olduğunu belirtir. Yarış koşullarına hiç bağışık değil.
Jared Smith

1
@JaredSmith hala benim açımdan. FP ve Immutability, çok iş parçacıklı ortamlarda veri bozulmasını ve / veya kaynak kilitlerini önlemek için güçlü yararlı paradigmalardır, ancak tek iş parçacıklı olduğu için JavaScript'te böyle değildir. Bilgeliğin bazı kutsal külçesini kaçırmadıkça, burada ana takas, yarış koşullarından kaçınmak için sizi daha karmaşık (ve daha yavaş) kodlamaya hazır olup olmamanızdır ... bu, çoğu insandan çok daha az sorun düşünüyorum.
Steven de Salas

37

Diğer cevaplar iyi olsa da, pratik bir kullanım durumu (diğer yanıtlardaki yorumlardan) hakkındaki sorunuza cevap vermek için bir dakika boyunca koşu kodunuzun dışına çıkmanıza ve burnunuzun hemen altındaki her yerde bulunan cevaba bakmanıza izin verin: git . Her taahhütte bulunduğunuzda depodaki verilerin üzerine yazmış olsaydınız ne olurdu ?

Şimdi değişmez koleksiyonların karşılaştığı sorunlardan birindeyiz: hafıza şişmesi. Git, her değişiklik yaptığınızda dosyaların yeni kopyalarını oluşturmayacak kadar zekidir, sadece farkları takip eder .

Git'in iç işleri hakkında çok şey bilmesem de, sadece referans aldığınız kütüphanelere benzer bir strateji kullandığını varsayabilirim: yapısal paylaşım. Kaputun altında kütüphaneler sadece farklı düğümleri izlemek için denemeler veya başka ağaçlar kullanırlar.

Logaritmik zamanda çalışan iyi bilinen ağaç işlem algoritmaları olduğundan , bu strateji bellek içi veri yapıları için de makul bir performans gösterir .

Başka bir kullanım örneği: web uygulamanızda geri alma düğmesi istediğinizi varsayalım. Verilerinizin değişmez gösterimleri ile, bunu uygulamak nispeten önemsizdir. Ancak mutasyona güveniyorsanız, bu, dünyanın durumunu önbelleğe alma ve atomik güncellemeler yapma konusunda endişelenmeniz gerektiği anlamına gelir.

Kısacası, çalışma zamanı performansı ve öğrenme eğrisindeki değişmezlik için bir bedel vardır. Ancak deneyimli herhangi bir programcı, hata ayıklama zamanının kod yazma süresinin büyüklüğüne göre ağır bastığını söyleyecektir. Ayrıca, çalışma zamanı performansındaki hafif isabet, büyük olasılıkla kullanıcılarınızın dayanması gerekmeyen durumla ilgili hatalardan daha ağır basmaktadır.


1
Harika bir örnek diyorum. Değişmezlik anlayışım şimdi daha açık. Teşekkürler Jared. Aslında uygulamalardan biri UNDO düğmesi: D Ve işleri benim için oldukça basitleştirdiniz.
bozzmob

3
Git'te mantıklı olan bir desenin her yerde aynı şeyin mantıklı olduğu anlamına gelmemesi. Git'te saklanan tüm geçmişi önemsiyorsunuz ve farklı dalları birleştirmek istiyorsunuz. Ön uçta, devlet tarihinin çoğunu umursamazsınız ve tüm bu karmaşıklığa ihtiyacınız yoktur.
Kayak

2
@Ski sadece karmaşık çünkü varsayılan değil. Projelerimde genellikle mori veya immutable.js kullanmıyorum: Her zaman üçüncü taraf borçlarını almaktan çekiniyorum. Ama bu varsayılan (la clojurescript) veya en azından bir tercihli yerel seçeneği olsaydı, her zaman kullanardım, çünkü örneğin clojure'da programladığımda her şeyi hemen atomlara doldurmuyorum.
Jared Smith

Joe Armstrong performans hakkında endişelenme der, sadece birkaç yıl bekleyin ve Moore yasası sizin için bununla ilgilenecektir.
ximo

1
@JaredSmith Haklısınız, işler sadece küçülüyor ve kaynak kısıtlanıyor. Bunun JavaScript için sınırlayıcı faktör olup olmayacağından emin değilim. Performansı arttırmak için yeni yollar bulmaya devam ediyoruz (örneğin Svelte). Bu arada, diğer yorumunuza tamamen katılıyorum. Değişmez veri yapılarını kullanmanın karmaşıklığı veya zorluğu, genellikle, kavramı yerleşik olarak desteklemeyen dilde ortaya çıkar. Clojure değişmezliği basitleştirir çünkü dile pişirilir, tüm dil fikir etrafında tasarlanmıştır.
ximo

9

Soru şu: Değişmezlik neden bu kadar önemli? Nesneleri değiştirirken yanlış olan ne? İşleri basitleştirmiyor mu?

Değişebilirlik hakkında

Değişkenlikte teknik açıdan hiçbir şey yanlış değildir. Hızlı, hafızayı yeniden kullanıyor. Geliştiriciler bunu baştan beri kullanıyorlar (hatırladığım gibi). Bu kullanımın getirebileceği değişiklik ve sıkıntıların kullanımında sorun vardır.

Nesne herhangi bir şeyle paylaşılmazsa, örneğin fonksiyonun kapsamında bulunur ve dışarıya maruz kalmazsa, değişmezliğin faydalarını görmek zordur. Gerçekten bu durumda değişmez olmak mantıklı değil. Değişmezlik duygusu bir şey paylaşıldığında başlar.

Değişebilirlik baş ağrısı

Değişken paylaşılan yapı kolayca birçok tuzak oluşturabilir. Kodun referansa erişimi olan herhangi bir parçasındaki herhangi bir değişiklik, bu referansın görünürlüğü olan diğer kısımları etkilemektedir. Bu etki, farklı modüllerden haberdar olmamalılar bile tüm parçaları birbirine bağlar. Bir fonksiyondaki mutasyon, uygulamanın tamamen farklı bir bölümünü kilitleyebilir. Böyle bir şey kötü bir yan etkidir.

Sonraki sık sık mutasyon sorunu bozuk durumu. Mutasyon prosedürü ortada başarısız olduğunda ve bazı alanlar değiştirilirken bazıları değiştirilmediğinde bozuk durum oluşabilir.

Dahası, mutasyonla değişimi izlemek zordur. Basit referans kontrolü, bazı derin kontrollerin neyin değiştiğini bilmek için farkı göstermeyecektir. Ayrıca değişimi izlemek için bazı gözlemlenebilir modellerin tanıtılması gerekir.

Son olarak, mutasyon güven açığının sebebidir. Eğer mutasyona uğratılabilirse, bazı yapıların değer istediğinden nasıl emin olabilirsiniz.

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

Yukarıdaki örnekte görüldüğü gibi, değişebilir yapıyı geçmek her zaman farklı bir yapıya sahip olarak bitebilir. DoSomething işlevi dışarıdan verilen özniteliği değiştiriyor. Kod için güven yok, gerçekten neye sahip olduğunuzu ve neye sahip olacağınızı bilmiyorsunuz. Bütün bu problemler cereyan etmektedir, çünkü: Değişebilir yapılar hafızaya işaretçilerdir.

Değişmezlik değerlerle ilgilidir

Değişmezlik, değişimin aynı nesne, yapı üzerinde yapılmadığı, ancak değişimin yenisinde temsil edildiği anlamına gelir. Bunun nedeni, referansın yalnızca bellek işaretçisini değil değeri temsil etmesidir. Her değişiklik yeni bir değer yaratır ve eskisine dokunmaz. Bu tür açık kurallar, güven ve kod öngörülebilirliğini geri verir. İşlevleri kullanmak güvenlidir, çünkü mutasyon yerine kendi değerlerine sahip kendi sürümleriyle ilgilenirler.

Bellek kapları yerine değerlerin kullanılması, her nesnenin belirli değiştirilemez değeri temsil ettiğinden ve onu kullanmanın güvenli olduğundan emin olur.

Değişmez yapılar değerleri temsil eder.

Orta makalede konuya daha da fazla giriyorum - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310


6

JavaScript'te değişmezlik neden bu kadar önemli (veya gerekli)?

Değişmezlik farklı bağlamlarda izlenebilir, ancak en önemlisi onu uygulama durumuna ve uygulama arayüzüne karşı izlemek olacaktır.

JavaScript Redux modelini çok şık ve modern bir yaklaşım olarak göreceğim ve bundan bahsettiğiniz için.

Kullanıcı arayüzü için öngörülebilir yapmamız gerekiyor . Eğer tahmin edilebilir UI = f(application state).

Uygulamalar (JavaScript'te), indirgeyici işlevi kullanılarak gerçekleştirilen eylemlerle durumu değiştirir .

İndirgeyici işlevi yalnızca eylemi ve eski durumu alır ve eski durumu olduğu gibi bırakarak yeni durumu döndürür.

new state  = r(current state, action)

resim açıklamasını buraya girin

Avantajı: tüm durum nesneleri kaydedildiğinden eyaletlere zaman yolculuğu yaparsınız ve o zamandan beri uygulamayı herhangi bir durumda oluşturabilirsiniz UI = f(state)

Böylece kolayca geri alabilir / tekrarlayabilirsiniz.


Tüm bu durumları yaratmak hala bellek verimli olabilir, Git ile bir benzetme harika ve Linux işletim sisteminde sembolik bağlantılarla (inode'lara dayanarak) benzer bir benzetmeye sahibiz.


5

Javascript'teki Dokunulmazlığın bir diğer yararı, genel olarak tasarım için önemli faydaları olan Geçici Kuplajı azaltmasıdır. Bir nesnenin arayüzünü iki yöntemle düşünün:

class Foo {

      baz() {
          // .... 
      }

      bar() {
          // ....
      }

}

const f = new Foo();

Bir çağrı bu duruma baz()bir çağrı için geçerli bir durumda nesneyi almak için gerekli olan bar()doğru işe. Ama bunu nasıl biliyorsun?

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

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

Bunu anlamak için, sınıfsal içselleri incelemelisiniz, çünkü genel arabirimi incelemekten hemen anlaşılmaz. Bu sorun, çok sayıda değişebilir durum ve sınıf içeren büyük bir kod tabanında patlayabilir.

Eğer Foo değişmezdir sonra bu artık bir sorundur. Sınıfın iç durumu değişemeyeceği için arayabileceğimizi bazveya barherhangi bir sırayla yapabileceğimizi varsaymak güvenlidir .


4

Bir zamanlar, iş parçacıkları arasında veri senkronizasyonu ile ilgili bir sorun vardı. Bu sorun büyük bir acıydı, 10'dan fazla çözüm vardı. Bazı insanlar bunu radikal bir şekilde çözmeye çalıştı. İşlevsel programlamanın doğduğu bir yerdi. Tıpkı Marksizm gibi. Dan Abramov'un bu fikri JS'ye nasıl sattığını anlayamadım, çünkü tek iş parçacıklı. O bir dahi.

Küçük bir örnek verebilirim. __attribute__((pure))Gcc'de bir özellik var . Derleyiciler, özel olarak çözmezseniz, işlevinizin saf olup olmadığını çözmeye çalışır. Durumunuz değiştirilebilir olsa bile işleviniz saf olabilir. Değişmezlik, işlevinizin saf olacağını garanti etmenin 100'den fazla yolundan sadece biridir. Aslında fonksiyonlarınızın% 95'i saf olacaktır.

Gerçekten ciddi bir nedeniniz yoksa herhangi bir sınırlama (değişmezlik gibi) kullanmamalısınız. Bazı durumları "Geri Almak" istiyorsanız, işlemler oluşturabilirsiniz. İletişimi basitleştirmek istiyorsanız, değişmez veriler içeren olaylar gönderebilirsiniz. Sana bağlı.

Bu mesajı Marksizm sonrası cumhuriyetten yazıyorum. Herhangi bir fikrin radikalleşmesinin yanlış bir yol olduğuna eminim.


3. paragraf çok mantıklı. Bunun için teşekkür ederim. 'Bazı durum "Geri almak" istiyorsanız, işlemler oluşturabilirsiniz' !!
bozzmob

Bu arada Marksizm ile karşılaştırma OOP için de yapılabilir. Java'yı hatırlıyor musunuz? Heck, JavaScript'in Java'nın tuhaf parçaları? Hype asla iyi değildir, radikalleşmeye ve kutuplaşmaya neden olur. Tarihsel olarak, OOP, Facebook'un Redux hippinginden çok daha hiperdi. Gerçi onlar ellerinden geleni yaptılar.
ximo

4

Farklı Bir Alım ...

Diğer cevabım soruyu çok pratik bir bakış açısıyla ele alıyor ve hala beğendim. Bunu buna bir zeyilname yerine başka bir cevap olarak eklemeye karar verdim, çünkü umarım soruyu da cevaplayan sıkıcı bir felsefi ranttır, ancak mevcut cevabımla gerçekten uymuyor.

TL; DR

Küçük projelerde bile değişmezlik yararlı olabilir, ancak var olduğu için bunun sizin için olduğunu varsaymayın.

Çok, çok daha uzun cevap

NOT: Bu cevabın amacı için, 'disiplin' kelimesini bazı yararlar için kendini inkar etmek için kullanıyorum.

Bu, başka bir soruya benzer: "Typcript kullanmalı mıyım? JavaScript'te türler neden bu kadar önemlidir?". Benzer bir cevabı var. Aşağıdaki senaryoyu düşünün:

Yaklaşık 5000 satırlık bir JavaScript / CSS / HTML kod tabanının tek yazarı ve koruyucususunuz. Yarı teknik patronunuz, yeni sıcaklık olarak Typescript hakkında bir şeyler okur ve ona taşınmak isteyebileceğimizi önerir, ancak kararı size bırakır. Yani onu okudunuz, onunla oynayın, vb.

Şimdi bir seçim yapma seçeneğiniz var, dizgiye mi geçiyorsunuz?

Dakik yazmanın bazı zorlayıcı avantajları vardır: akıllı, erken hataları yakalama, API'larınızı önceden belirleme, bunları yeniden düzenlerken bir şeyi düzeltme kolaylığı, daha az test. Dizginin de bazı maliyetleri vardır: bazı çok doğal ve doğru JavaScript deyimleri, özellikle güçlü olmayan tip sisteminde modellemek zor olabilir, ek açıklamalar LoC'yi, mevcut kod tabanını yeniden yazma zamanını ve çabasını, yapı hattında ekstra adım vb. Daha temelde, kodunuzun doğru olma ihtimali vaat eden olası doğru JavaScript programlarının bir alt kümesini yürütür . İsteğe bağlı olarak kısıtlayıcıdır. Bütün mesele bu: Sizi sınırlayan bir disiplin yüklüyorsunuz (umarım kendinizi ayağınızdan vurmaktan).

Yukarıdaki paragraf bağlamında yeniden ifade edilen soruya geri dönelim: buna değer mi ?

Açıklanan senaryoda, küçük-orta-ölçekli bir JS kod tabanına çok aşina iseniz, Typcript kullanma seçiminin pratik olmaktan daha estetik olduğunu iddia ediyorum. Ve bu iyi , estetikte yanlış bir şey yok , sadece zorunlu değiller.

Senaryo B:

İşleri değiştiriyorsunuz ve şimdi Foo Corp'ta bir iş kolu programcısısınız. Babil, webpack'i içeren oldukça karmaşık bir yapı hattı olan 90000 LoC (ve sayıyor) JavaScript / HTML / CSS kod tabanı üzerinde 10 kişilik bir ekiple çalışıyorsunuz , bir çoklu dolgular paketi, çeşitli eklentiler, bir devlet yönetim sistemi, ~ 20 üçüncü taraf kitaplığı, ~ 10 dahili kitaplık, kurum içi stil kılavuzu kurallarına sahip bir linter gibi editör eklentileri vb.

5K LoC erkek / kız olduğunuzda, bu kadar önemli değildi. Hatta dokümantasyon değildi o büyük bir mesele, 6 ay sonra da belirli bir kod kısmına bile önümüzdeki geri kolayca yeterince anlamaya başladı. Ama şimdi disiplin sadece hoş değil aynı zamanda gerekli . Yani disiplin typescript dahil olmayabilir, ama olacak büyük ihtimalle statik analiz çeşit yanı bütün olarak kodlama disiplin (dokümantasyon, stil rehberi, yapı komut, regresyon testleri, CI) diğer formları içerir. Disiplin artık bir lüks değil, bir zorunluluktur .

Tüm bunlar GOTO1978'de uygulandı : C'deki küçük küçük blackjack oyununuz GOTOs ve spagetti mantığını kullanabilir ve kendi macerasını seçmek için büyük bir anlaşma değildi, ancak programlar büyüdükçe ve programlar büyüdükçe daha iddialı, iyi, disiplinsiz kullanımı GOTOsürdürülemedi. Ve tüm bunlar bugün değişmezlik için geçerlidir.

Statik tiplerde olduğu gibi, onu koruyan / genişleten bir mühendis ekibiyle büyük bir kod tabanı üzerinde çalışmıyorsanız, değişmezliği kullanma seçeneği pratik olmaktan daha estetiktir: faydaları hala vardır, ancak maliyetlerden daha ağır basmayabilir.

Ancak tüm yararlı disiplinlerde olduğu gibi, artık isteğe bağlı olmadığı bir nokta geliyor. Sağlıklı bir kiloyu korumak istiyorsam, dondurma içeren disiplin isteğe bağlı olabilir. Ancak rekabetçi bir sporcu olmak istersem, dondurma yemleyip yememem konusundaki seçimim, hedef seçimimden kaynaklanıyor. Dünyayı yazılımla değiştirmek istiyorsanız, değişmezlik kendi ağırlığı altında çökmesini önlemek için ihtiyaç duyduğunuz şeyin bir parçası olabilir.


1
+1 Beğendim. Jared üzerinde çok daha fazlası. Yine de değişmezlik, bir ekibi kendi disiplin eksikliğinden kurtaramaz. 😉
Steven de Salas

@StevendeSalas bir tür disiplindir. Ve bu yüzden, bunun diğer yazılım mühendisliği disiplini biçimleriyle ilişkili olduğunu (ancak bunun yerine geçmediğini) düşünüyorum. Takviye yerine tamamlayıcıdır. Ama cevabınızla ilgili bir yorumda söylediğim gibi, hepsi aynı dev kod tabanında taşlayan mühendislerden oluşan bir teknoloji devi tarafından itildiğine şaşırmadım :) alabilecekleri tüm disipline ihtiyaçları var. Çoğunlukla nesneleri mutasyona uğratmıyorum ama aynı zamanda herhangi bir uygulama biçimi de kullanmıyorum, çünkü bu sadece benim.
Jared Smith

0

Ben libs (redux, vuex vb ...) gibi tüm bu değişmez depolama yerine geçebilir mutable (veya değişmez) durumu için bir çerçeve agnostik açık kaynak (MIT) lib oluşturdum.

Dokunulmaz durumlar benim için çirkindi çünkü yapılacak çok fazla iş vardı (basit okuma / yazma işlemleri için çok fazla eylem), kod daha az okunabilir ve büyük veri kümelerinin performansı kabul edilemezdi (tüm bileşen yeniden oluşturma: /).

İle derin devlet gözlemci ben nokta gösterimi ve kullanım joker ile tek düğüm güncelleyebilirsiniz. Ayrıca sadece değiştirilmiş somut değerleri {path:value}= daha az bellek kullanımı tutarak devletin tarihini de oluşturabilirim (geri al / yinele / zaman yolculuğu) .

İle derin devlet gözlemci ben ince ayar yapabiliyor can ve performans önemli ölçüde geliştirilebilir yüzden bileşeni davranışı üzerinde tahıl kontrole sahip. Kod daha okunabilir ve yeniden düzenleme çok daha kolaydır - sadece yol dizelerini arayın ve değiştirin (kodu / mantığı değiştirmeye gerek yoktur).


-1

Bence değişmez nesnelerin asıl nedeni, nesnenin durumunu geçerli tutmaktır.

Diyelim ki bir nesnemiz var arr. Bu nesne, tüm öğeler aynı harf olduğunda geçerlidir.

// 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

arrdeğişmez bir nesne haline gelirse , arr'ın her zaman geçerli bir durumda olduğundan emin olacağız.


arrHer aradığınızda mutasyona uğradığını düşünüyorumfillWithZ
rodrigo-silveira

immutable.js kullanırsanız, her değiştirişinizde nesnenin yeni bir kopyasını alırsınız. böylece orijinal nesne el değmeden
kalır
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.