不变性的逆向观点
TL/DR:在 JavaScript 中,不变性与其说是必需品,不如说是一种时尚趋势。如果您正在使用 React,它确实为状态管理中一些令人困惑的设计选择提供了一种巧妙的解决方法。然而,在大多数其他情况下,它不会为其引入的复杂性增加足够的value,更多地是为了充实简历而不是满足实际的客户需求。
长答案:阅读下文。
为什么不可变性在 javascript 中如此重要(或需要)?
好吧,我很高兴你问了!
前段时间,一个非常有才华的人Dan Abramov编写了一个名为Redux的 JavaScript 状态管理库,它使用纯函数和不变性。他还制作了一些非常酷的视频,使这个想法非常容易理解(和销售)。
时机恰到好处。Angular的新鲜感正在消退,JavaScript 世界已经准备好关注具有适当酷炫程度的最新事物,而这个库不仅具有创新性,而且与被另一家硅谷巨头兜售的React完美契合。
尽管很可悲,但时尚在 JavaScript 世界中占据主导地位。现在阿布拉莫夫被誉为半神,而我们凡人都必须服从不变之道……这是否有意义。
改变对象有什么问题?
没有什么!
事实上,程序员一直在改变对象……只要有要改变的对象。换句话说,50 多年的应用程序开发经验。
为什么要把事情复杂化?当您有对象cat
并且它死了时,您真的需要一秒钟cat
来跟踪更改吗?大多数人只会说cat.isDead = true
并完成它。
(变异对象)不会让事情变得简单吗?
是的!..当然可以!
特别是在 JavaScript 中,它在实践中最有用的是呈现在其他地方(例如在数据库中)维护的某些状态的视图。
如果我有一个新的 News 对象需要更新怎么办?...在这种情况下我如何实现?删除商店并重新创建它?将一个对象添加到数组中不是一个成本更低的操作吗?
好吧,您可以采用传统方法并更新News
对象,因此该对象的内存表示会发生变化(以及向用户显示的视图,或者人们希望如此)......
或者替代...
您可以尝试性感的 FP/Immutability 方法,并将您对News
对象的更改添加到跟踪每个历史更改的数组中,这样您就可以遍历该数组并找出正确的状态表示应该是什么(呸!)。
我正在尝试了解这里的内容。请赐教我:)
时尚来来去去,伙计。有很多方法可以给猫剥皮。
很抱歉,您不得不忍受一组不断变化的编程范式的混乱。但是,嘿,欢迎来到俱乐部!!
现在有几个重要的点需要记住,关于不变性,你会以只有天真才能聚集的狂热强度向你抛出这些。
1) 不变性对于避免多线程环境中的竞争条件非常有用。
当多个线程想要更改对象时,多线程环境(如 C++、Java 和 C#)会犯锁定对象的做法。这对性能不利,但比数据损坏的替代方案要好。但还不如让一切都一成不变(上帝赞美 Haskell!)。
可惜!在 JavaScript 中,您总是在单线程上操作。甚至网络工作者(每个都在单独的上下文中运行)。因此,由于您的执行上下文中不能有与线程相关的竞争条件(所有那些可爱的全局变量和闭包),支持不变性的要点就消失了。
(话虽如此,有是一个优势,在网络工作者,这是,你有没有关于与主线程上的对象摆弄的预期使用纯函数。)
2)不变性可以(以某种方式)避免应用程序状态下的竞争条件。
这就是问题的真正症结所在,大多数(React)开发人员会告诉你,Immutability 和 FP 可以以某种方式发挥这种魔法,让你的应用程序的状态变得可预测。
当然,这并不意味着您可以避免数据库中的竞争条件,要解决这个问题,您必须协调所有浏览器中的所有用户,为此您需要一种后端推送技术,如WebSockets(更多关于此的内容),它将向运行该应用程序的每个人广播更改。
也不意味着 JavaScript 中存在一些固有问题,即您的应用程序状态需要不变性才能变得可预测,任何在 React 之前编写前端应用程序的开发人员都会告诉您这一点。
这个相当令人困惑的声明只是意味着,如果您使用 React,您的应用程序很容易出现竞争条件,但这种不变性可以让您消除这种痛苦。为什么?因为 React 是特殊的......它首先被设计为一个高度优化的渲染库,状态管理颠覆了这个目标,因此组件状态是通过一个异步事件链(又名“单向数据绑定”)来管理的,这些事件优化了渲染,但您无法控制并依靠您记住不要直接改变状态......
鉴于这种情况,很容易看出对不变性的需求与 JavaScript 几乎没有关系,而与 React 有很大关系:如果在你的新应用程序中有一堆相互依赖的变化,并且没有简单的方法来弄清楚你的state 当前处于,你会感到困惑,因此使用不变性来跟踪每个历史变化是非常有意义的。
3) 竞争条件绝对糟糕。
好吧,如果您使用 React,它们可能是。但是如果你选择不同的框架,它们就很少见了。
此外,你通常有更大的问题要处理......像依赖地狱这样的问题。就像一个臃肿的代码库。就像你的 CSS 没有被加载一样。就像一个缓慢的构建过程或被困在一个单一的后端,这使得迭代几乎不可能。就像没有经验的开发人员不了解正在发生的事情并把事情搞得一团糟一样。
你懂。现实。但是,嘿,谁在乎呢?
4) 不变性利用引用类型来减少跟踪每个状态变化对性能的影响。
因为说真的,如果你每次状态改变时都要复制东西,你最好确保你很聪明。
5) 不变性允许你撤销东西。
因为呃……这是你的项目经理会要求的第一项功能,对吧?
6) 不可变状态与 WebSockets 结合有很多很酷的潜力
最后但并非最不重要的一点是,状态增量的积累与 WebSockets 相结合是一个非常引人注目的案例,它允许将状态作为不可变事件流轻松消耗......
一旦在这个概念上花一分钱(状态是一个事件流——而不是一组代表最新观点的粗略记录),不变的世界就变成了一个神奇的栖息地。一个超越时间本身的事件来源的奇迹和可能性的土地。并在完成后右这个绝对可以让实时应用程式EASI呃来完成,你只播事件给大家感兴趣,所以他们的流动建立自己表示本和自己的变化写回社区流动。
但在某些时候,您醒来并意识到所有的奇迹和魔法都不是免费的。与你热切的同事不同,你的利益相关者(是的,付钱给你的人)很少关心哲学或时尚,而是很关心他们为打造可以销售的产品而支付的钱。最重要的是,它更难为不变性编码并且更容易破坏它,另外,如果您没有后端来支持它,那么拥有一个不变的前端就毫无意义。当(并且如果!)您最终说服您的利益相关者您应该通过像 WebSockets 这样的推送技术发布和使用事件时,您会发现在生产中扩展是多么痛苦。
现在给一些建议,你是否应该选择接受它。
使用 FP/Immutability 编写 JavaScript 的选择也是使您的应用程序代码库更大、更复杂和更难管理的选择。我强烈主张将这种方法限制在您的 Redux 减速器上,除非您知道自己在做什么……而且如果您无论如何都要继续使用不变性,那么将不变状态应用于整个应用程序堆栈,而不仅仅是应用程序堆栈客户端,否则您将错过它的真正value。
现在,如果你有幸能够在你的工作中做出选择,那么试着运用你的智慧(或不),做支付你报酬的人应该做的事情。你可以根据你的经验、直觉或你周围发生的事情(诚然,如果每个人都在使用 React/Redux,那么有一个有效的论点是,找到一个资源来继续你的工作会更容易)。或者,您可以尝试恢复驱动开发或炒作驱动开发方法。他们可能更适合你。
总之,可以说对于不变性的事情是,它会令你的时尚与您同行,至少直到下一个热潮来临时,由此时你会很高兴地前进。
现在,在本次自我治疗之后,我想指出我已将此作为文章添加到我的博客 => JavaScript 中的不变性:逆势观点。如果您有强烈的感觉,也想摆脱胸膛,请随时在那里回复;)。