javascript - Immutable.js 关系

标签 javascript reactjs immutable.js

想象一下,约翰有两个 child 爱丽丝和鲍勃,鲍勃有一只猫猎户座。

var Immutable = require('immutable');

var parent = Immutable.Map({name: 'John'});
var childrens = Immutable.List([
    Immutable.Map({name: 'Alice', parent: parent}),
    Immutable.Map({name: 'Bob', parent: parent})
]);
var cat = Immutable.Map({name: 'Orion', owner: childrens.get(1)});

几年后,约翰想改名为简。
var renamedParent = parent.set('name', 'Jane');

...并让 children 知道它。
childrens = childrens.map(function(children) {
    children.set('parent', renamedParent);
});

然后我必须更新 cat 因为 Bob 改变了。
cat = cat.set('owner', childrens.get(1));

当一个对象发生变化时,是否可以自动更新所有相关对象?我看了Cursors ,但我不确定它们是否是解决方案。如果可能的话,你能给我举个例子吗?

最佳答案

转述问题:

当一个对象在不可变集合中发生变化时,是否可以自动更新所有相关对象?

简答

不。

长答案

不,但在不可变的数据结构中没有任何变化,所以这不是问题。

更长的答案

比较复杂...

不变性

不可变对象(immutable对象)的全部意义在于,如果您有对不可变对象(immutable对象)的引用,您就不必费心检查它的任何属性是否已更改。那么,这不是问题吗?好...

结果

这会产生一些后果 - 它们是好是坏取决于您的期望:

  • pass-by-value之间没有区别和 pass-by-reference语义
  • 一些比较可以更容易
  • 当您在某处传递对对象的引用时,您不必担心代码的其他部分会更改它
  • 当您从某处获得对某个对象的引用时,您知道它永远不会改变
  • 您避免了一些并发问题,因为没有时间变化的概念
  • 当没有任何变化时,您不必担心变化是否是原子性的
  • 更容易实现 software transactional memory (STM) 具有不可变数据结构

  • 但世界是可变的

    当然,在实践中我们经常处理随时间变化的值。看起来不可变状态不能描述可变世界,但人们有一些处理它的方法。

    以这种方式看待它:如果您在某个 ID 上有您的地址并且您搬到了另一个地址,则该 ID 应该更改为与新的真实数据一致,因为您住在该地址不再是真实的。但是,当您购买包含您地址的东西时收到发票,然后您更改了地址,发票将保持不变,因为在编写发票时您住在该地址仍然是正确的。现实世界中的某些数据表示是不可变的,例如该示例中的发票,而有些则是可变的,例如 ID。

    现在以您的示例为例,如果您选择使用不可变结构来建模您的数据,您必须以一种您从我的示例中考虑发票的方式来考虑它。数据可能不是最新的,但它在某个时间点始终是一致和真实的,并且永远不会改变。

    如何应对变化

    那么如何用不可变数据对变化进行建模呢?在 Clojure 中有一个很好的解决方法使用 Vars , Refs , Atoms , 和 Agents , 和 ClojureScript (针对 JavaScript 的 Clojure 编译器)支持其中的一些(特别是 Atom 应该像在 Clojure 中一样工作,但没有 Refs、STM、Vars 或 Agents - 参见 what are the differences between ClojureScript and Clojure regarding concurrency features)。

    看着 how Atoms are implemented in ClojureScript似乎您可以使用普通的 JavaScript 对象来实现相同的功能。它适用于诸如拥有本身可变的 JavaScript 对象之类的事情,但它有一个属性是对不可变对象(immutable对象)的引用 - 您将无法更改该不可变对象(immutable对象)的任何属性,但您将能够构造一个不同的不可变对象(immutable对象),并将旧的对象交换到顶级可变对象中的新对象。

    其他语言如 Haskellpurely functional可能有不同的方式来处理可变的世界,比如 monads (一个众所周知的难以解释的概念 - Douglas Crockford,JavaScript: The Good Partsdiscoverer of JSON 的作者在他的演讲 Monads and Gonads 中将其归因于“一元诅咒”)。

    你的问题看似简单,其实涉及的问题很复杂。当然,如果对您的问题回答“否”是否可以在一个对象发生更改时自动更新所有相关对象,那当然会错过这一点,但它比这更复杂,并且说在不可变对象(immutable对象)中没有任何变化(所以这个问题永远不会发生)同样无济于事。

    可能的解决方案

    您可以拥有一个顶级对象或变量,您始终可以从中访问所有结构。假设你有:
    var data = { value: Immutable.Map({...}) }
    

    如果您总是使用 data.value 访问您的数据(或一些更好的名字)然后你可以通过 data到代码的其他部分,每当您的状态发生变化时,您只需将一个新的 Immutable.Map({...}) 分配给您的 data.value那时你所有使用 data 的代码将获得新值。

    如何以及何时更新 data.value可以通过使其从用于更新状态的 setter 函数自动触发来解决新的不可变结构。

    另一种方法是在每个结构的级别使用类似的技巧,例如 - 我使用变量的原始拼写:
    var parent = {data: Immutable.Map({name: 'John'}) };
    var childrens = {data: Immutable.List([
        Immutable.Map({name: 'Alice', parent: parent}),
        Immutable.Map({name: 'Bob', parent: parent})
    ])};
    

    但是你必须记住,你所拥有的值不是不可变结构,而是那些引用了不可变结构的附加对象,这些对象引入了额外的间接级别。

    一些阅读

    我的建议是查看其中的一些项目和文章:
  • Immutable React Peter Hausel 的文章
  • Morearty.js - 在 React 中使用不可变状态就像在 Om 中一样,但用纯 JavaScript 编写
  • react-cursor - 用于 Facebook 的功能状态管理抽象
  • Omniscient - 一个为 React 组件提供抽象的库,允许快速自顶向下渲染包含不可变数据
    * Om - React 的 ClojureScript 接口(interface)
  • mori - 在 JavaScript 中使用 ClojureScript 的持久数据结构的库
  • Fluxy - Facebook 的 Flux 架构的实现
  • Facebook's Immutable - 与 Facebook React 结合使用的 JavaScript 持久数据集合和 Facebook Flux面临您遇到的类似问题(搜索如何将 Immutable 与 React 和 Flux 结合可能会给您一些好主意)

  • 我希望这个答案即使没有给出简单的解决方案也会有所帮助,因为用不可变结构描述可变世界是一个非常有趣和重要的问题。

    其他方法

    有关不变性的不同方法,使用不可变对象(immutable对象)作为不一定是常量的数据的代理,请参阅 Immutable Objects vs. Common Sense Yegor Bugayenko 和他的文章的网络研讨会:
  • Objects Should Be Immutable
  • How Immutability Helps
  • How an Immutable Object Can Have State and Behavior?
  • Immutable Objects Are Not Dumb

  • Yegor Bugayenko 使用术语“不可变”的含义与函数式编程上下文中通常的含义略有不同。他不使用不可变或持久的数据结构和函数式编程,而是提倡使用面向对象编程的原始意义,即您实际上从未真正改变任何对象,但您可以要求它改变某些状态,或改变某些数据,它本身被认为是与对象分开的。很容易想象一个与关系数据库对话的不可变对象(immutable对象)。对象本身可以是不可变的,但它仍然可以更新存储在数据库中的数据。很难想象存储在 RAM 中的某些数据可以被认为与数据库中的数据一样与对象分离,但实际上并没有太大区别。如果您将对象视为暴露某些行为的自治实体,并且您尊重它们的抽象边界,那么将对象视为与数据不同的东西实际上很有意义,然后您就可以拥有具有可变数据的不可变对象(immutable对象)。

    如果有什么需要澄清的,请发表评论。

    关于javascript - Immutable.js 关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27446960/

    相关文章:

    javascript - 如何使用javascript的reduce方法使数组唯一

    reactjs - React Antd v4 自动完成在选择选项时显示选定的值而不是标签

    javascript - 不可变的js更新深层结构

    javascript - 保持 Immutable JS Map 的顺序

    javascript - getaddrinfo ENOTFOUND https ://graph. facebook.com/

    javascript - 按钮的onclick函数没有被调用

    javascript - 如何在循环中推送对象数组中的值?

    javascript - getIn() 在 Immutable.js 中做了什么?

    java - 禁用表结构

    javascript - 如何将一种 Backbone 模型与另一种 Backbone 模型关联起来?