为什么在JavaScript中不可变性如此重要(或必要)?

10 浏览
0 Comments

为什么在JavaScript中不可变性如此重要(或必要)?

我目前正在使用React JSReact Native框架进行开发。在过程中,我遇到了不可变性或Immutable-JS库,这是我在阅读Facebook的Flux和Redux实现时遇到的。

问题是,为什么不可变性这么重要?更改对象有什么问题吗?它不能简化事情吗?

举个例子,我们考虑一个简单的新闻阅读器应用程序,起始屏幕是新闻标题的列表视图。

如果我设置一个包含对象数组的值最初,我就不能操纵它。这就是不可变性原则所说的,对吗?(如果我错了,请纠正我。)

但是,如果我有一个新的要更新的新闻对象呢?在通常情况下,我只需将对象添加到数组中即可。

在这种情况下,我该怎么做呢?删除存储并重新创建吗?

将对象添加到数组中不是一种更便宜的操作吗?

admin 更改状态以发布 2023年5月21日
0
0 Comments

Yes, adding an object to an array is definitely less expensive than recreating the entire store. And in most cases, mutating objects is perfectly fine and simple.

However, there are some situations where immutability can be helpful. For example, in React, using immutable data can help prevent unnecessary re-renders and improve performance. It also provides a workaround for some confusing design choices in state management.

But outside of React and a few other specific situations, immutability is more of a trend than a necessity. It can add unnecessary complexity without providing significant benefits.

So, while it's important to know and understand immutability, it's also important to use it judiciously and not just follow the fashion trend of the moment.

嗯,你可以采取传统的方式并更新News对象,这样你的对象在内存中的表示就会改变(这样用户展示的视图,或者希望如此)...

另外...

你可以尝试时髦的FP/Immutable方法,将你对 News 对象的更改添加到跟踪每个历史更改的数组中,然后遍历该数组并确定正确的状态表示应该是什么(哇)。

我正在尝试学习哪种方式是正确的。请 enlighten 我 🙂

潮流来来去去,兄弟。有很多种方法来达成同样的结果。

很抱歉你要承受不断变化的编程范式的混乱。但是嘿,欢迎来到我们的club!!

现在有几个重要的要点需要记住,关于Immutability,你将会以只有天真的热情才能表达的热切强度得到这些要点。

1)在多线程环境中,Immutability有助于避免竞争条件

多线程环境(如C ++,Java和C#)会锁定对象,当多个线程想要更改它们时。这对性能来说很糟糕,但比数据损坏的代价要好。但是不如使所有东西都是immutable(赞颂Haskell的主啊!)。

但苦呀!在JavaScript中,您总是在一个单线程上操作

(在每个单独的上下文中运行的Web Worker。)因此,由于您无法在执行上下文中产生与线程相关的竞争条件(所有这些可爱的全局变量和闭包),因此Immutability支持的主要观点被搁置。

(话虽如此,在Web Worker中使用纯函数是有好处的,原因是你对主线程上的对象没有什么期望。)

2) Immutability可以(以某种方式)避免您应用程序状态的竞争条件。

这是问题的真正关键,大多数(React)开发人员会告诉您,Immutability和FP可以以某种方式使用这种魔法,使您的应用程序状态变得可预测。

当然,这并不意味着您可以避免在数据库中的竞争条件,要实现这一点,您需要协调所有浏览器中的所有用户,对于这一点,您需要像WebSockets这样的后端推技术(下面将更详细地介绍)它将向每个运行应用程序的人广播变化。

这并不意味着JavaScript有某种固有的问题,需要使用不变性才能实现可预测的应用状态,任何在React之前编写前端应用程序的开发人员都会告诉你这一点。

这个相当令人困惑的说法的意思是,如果你使用React,你的应用程序容易受到竞争条件的影响,但不变性可以让你摆脱这种痛苦。为什么?因为React很特别..它首先被设计为高度优化的渲染库,状态管理被放在了次要的位置,因此组件状态通过异步事件链(又称“单向数据绑定”)来管理,这优化了渲染,但你无法控制并且依赖于你记住不要直接改变状态..

在这种情况下,很容易看出,不变性的需求与JavaScript无关,与React有很大关系:如果在你的全新应用程序中有一堆相互依赖的变化,并且没有简单的方法来确定你的状态当前处于什么状态,你会感到困惑,因此使用不变性来跟踪每一个历史变化是完全有道理的。

3)竞争条件是绝对不好的。

好吧,如果你使用React的话,它们可能会是这样,但如果你选择其他框架,它们就不常见。

此外,你通常有更大的问题要处理...像依赖地狱,像臃肿的代码库,像你的CSS没有加载。像慢的构建过程或被困在使迭代几乎不可能的单块式后端。像没有经验的开发人员不理解正在发生的事情,把事情搞砸。

你知道的。现实。但是,谁在乎呢?

4)不变性使用引用类型来减少跟踪每个状态更改的性能影响。

因为说真的,如果你每次状态改变都要复制东西,你最好确保你聪明些。

5)不变性允许你撤销东西。

因为嗯...这是你的项目经理最需要的第一大特性,对吧?

6)在WebSockets中,不可变状态具有许多酷炫的潜力

最后但并非最不重要的一点是,使用WebSockets与状态差量的累积结合使用可以很容易地消费不可变事件的流作为状态,这一点非常具有说服力。一旦你意识到这个概念(即状态是事件流而不是最新视图的一组粗略记录),不可变世界将成为一个神奇的居住之地。一个超越时间本身的充满奇妙可能性的事件源国度。如果做得好,这肯定可以使实时应用程序更容易实现,你只需向所有感兴趣的人广播事件流,并让他们构建自己的表示,并将自己的更改写回到公共流中。

但是,在某个时候,你会醒悟过来,所有的神奇和魔法都不是免费的。与你热情的同事不同,你的利益相关者(是的,那些付钱给你的人)对哲学或时尚毫不在意,而对他们为构建可销售的产品支付的费用非常在意。底线是为不可变性编写代码更加困难,而破坏它更加容易,此外如果没有支持它的后端,那么拥有一个不可变前端也就毫无意义了。当(如果!)你最终说服你的利益相关者应该通过推送技术如WebSockets来发布和消费事件时,你会发现在生产环境中扩展其是多么的痛苦


现在是一些建议,如果你愿意采纳。

选择使用FP /不可变性编写JavaScript也是一种将应用程序代码库变得更大、更复杂和更难以管理的选择。 我强烈建议将此方法限制在你的Redux reducer中,除非你知道你在做什么......如果你要无论如何使用不可变性,那么就将不可变状态应用到整个应用程序堆栈中,而不仅仅是客户端。毕竟,拥有不可变前端,并将其连接到所有记录都具有单个可变版本的数据库是没有多大意义的......你只是回到了你试图摆脱的同样的问题!

现在,如果你足够幸运可以在你的工作中做出选择,则尝试使用你的智慧(或不使用)并为付你工资的人做正确的事情。你可以基于自己的经验、直觉或身边发生的事情来做决定(尽管如果每个人都在使用React/Redux,那么有一个有效的论据是找到继续工作的资源会更容易)......或者你可以尝试基于简历的开发炒作驱动开发方法。它们可能更适合你。

简言之,不变性的好处在于它可以让您与您的同行们保持时尚,至少在下一个狂热之前,到那时您会很乐意继续前进。


自我疗法后,我想指出,我已将此作为文章添加到我的博客中=> JavaScript中的不变性:异见之论。 如果您也想发表强烈感受,请在那里回复;)。

0
0 Comments

我最近一直在研究相同的话题。我会尽力回答你的问题,并尝试分享我所学到的知识。

问题是,为什么不可变性如此重要?改变对象有什么问题吗?不是这样做可以简化事情吗?

基本上,这归结于不可变性增加了可预测性、性能(间接),并允许进行突变跟踪。

可预测性

突变隐藏变化,这会产生(意想不到的)副作用,可能会导致讨厌的错误。当您强制执行不可变性时,您可以保持应用程序架构和心理模型简单,这使得更容易推断您的应用程序。

性能

即使将值添加到不可变对象中意味着需要创建新实例,其中需要复制现有值并在新对象中添加新值,而这会耗费内存,但不可变对象可以利用结构共享来减少内存开销。

所有更新都返回新值,但是内部结构共享可以显著减少内存使用量(和GC抖动)。这意味着,如果你将一个具有1000个元素的向量附加到向量中,它实际上不会创建一个长为1001个元素的新向量。最有可能,内部只分配了一些小对象。

您可以在此处阅读更多信息

突变跟踪

除了减少内存使用外,不可变性还允许您通过使用引用和值相等来优化应用程序。这使你非常容易看到是否有任何变化。例如,React组件中的状态更改。您可以使用shouldComponentUpdate通过比较状态对象来检查状态是否相同,从而防止不必要的呈现。

您可以在此处阅读更多信息

额外资源:

例如,如果我最初设定一个对象值的数组。我不能对它进行操作。这就是不变性原则,对吗?(如果我错了,请纠正我)。但是,如果我有一个新的新闻对象需要更新呢?通常情况下,我可以将对象添加到数组中。在这种情况下,我该怎么做?删除存储并重新创建吗?将对象添加到数组不是更少费用的操作吗?

是的,这是正确的。如果您对如何在应用程序中实现此功能感到困惑,我建议您查看redux如何处理此问题,以熟悉核心概念,这对我非常有帮助。

我喜欢使用Redux作为示例,因为它支持不可变性。它有一个单一的不可变状态树(称为store),在此树上,所有状态更改都是通过派发操作来显式实现的,这些操作由接受前一个状态及操作的reducer处理(每次一次),并返回您的应用程序的下一个状态。您可以在此处阅读有关它的核心原则的更多信息。

egghead.io上有一门出色的redux课程,Dan Abramov,redux的作者,解释了以下原则(我稍微修改了一下代码以更好地适应场景):

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 (
        
        
        
    { news.map( ({ title }) =>
  • { title }
  • ) }
); } }); // Handler that will execute when the store dispatches. const render = () => { ReactDOM.render( , document.getElementById('news') ); }; // Entry point. store.subscribe(render); render();

此外,这些视频还详细介绍了如何实现以下内容的不可变性:

0