当前位置: 首页 > 面试题库 >

Immutable.js关系

徐凌
2023-03-14
问题内容

想象一个情况,约翰有两个孩子爱丽丝和鲍勃,鲍勃有一只猫猎户座。

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

…让孩子们知道。

childrens = childrens.map(function(children) {
    children.set('parent', renamedParent);
});

然后我必须更新cat,因为Bob变了。

cat = cat.set('owner', childrens.get(1));

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


问题答案:

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

简短答案

没有。

长答案

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

更长的答案

比较复杂…

不变性

不可变对象的全部要点是,如果您引用了一个不可变对象,则无需费心检查其任何属性是否已更改。那么,这不是问题吗?好…

后果

这有一些后果-好坏取决于您的期望:

  • 按值传递语义和按引用传递语义之间没有区别
  • 一些比较会更容易
  • 当您在某处传递对对象的引用时,不必担心代码的其他部分会更改它
  • 当您从某个地方获得对对象的引用时,您知道它永远不会改变
  • 您避免了并发问题,因为没有时间变化的概念
  • 当没有任何变化时,您不必担心变化是否是原子的
  • 使用不可变的数据结构实现软件事务存储(STM)更容易

但是世界是可变的

当然,实际上,我们经常处理随时间变化的价值观。似乎不变的状态不能描述可变的世界,但是人们有一些处理它的方法。

这样看:如果您的地址上有一个ID,而您又移动到另一个地址,则应更改该ID以使其与新的真实数据保持一致,因为您住在该地址不再是真实的。但是,如果您在购买包含地址的商品时收到发票,然后更改地址,则发票保持不变,因为在写发票时,您确实住在该地址。现实世界中的某些数据表示形式是不可变的,例如该示例中的发票,而另一些则是可变的,例如ID。

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

如何应对变化

那么如何用不变的数据来建模变化呢?在Clojure中使用Vars,Refs,Atoms和Agents可以很好地解决它,而ClojureScript(针对JavaScript的Clojure编译器)支持其中的某些功能(特别是Atom应该像Clojure一样工作,但是有没有Ref,STM,Vars或Agents-请参阅ClojureScript和Clojure之间在并发功能方面的区别)。

看一下如何在ClojureScript中实现Atom,似乎您可以只使用普通的JavaScript对象来实现相同的目标。它可以用于具有可变的JavaScript对象之类的事情,但是它具有对不可变对象的引用的属性-
您将无法更改该不可变对象的任何属性,但可以构造一个不同的不可变对象,然后将顶级可变对象中的旧对象交换为新对象。

其他诸如Haskell之类的纯语言功能可能具有处理可变世界的不同方法,例如monads(一个难以解释的概念-
JavaScript :The Good
Parts

和JSON的作者Douglas Crockford,将其归因于“
monadic诅咒”。

您的问题似乎很简单,但它涉及的问题实际上却非常复杂。当然,对于一个对象是否可以在所有对象发生变化时自动更新所有相关对象的问题回答“否”,这显然是没有意义的,但这要复杂得多,并且说在不可变对象中,任何事物都不会改变(因此这个问题永远不会发生)将同样无济于事。

可能的解决方案

您可以有一个顶层对象或变量,您可以始终从中访问所有结构。假设您有:

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})
])};

但是随后您必须记住,值所具有的不是不变结构,而是引用了引入了附加间接级别的不变结构的那些附加对象。

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

其他方法

对于不可变性的另一种处理方法,即不可变对象是不一定恒定数据的代理,请参阅Yegor Bugayenko撰写的“
不可变对象与常识”网络研讨会及其文章:

  • 对象应该是不可变的
  • 不变性如何提供帮助
  • 不可变对象如何具有状态和行为?
  • 不可变的对象不是哑巴

Yegor
Bugayenko使用“不变”一词的含义与在函数式编程中通常所指的含义略有不同。他提倡使用术语的原始含义,而不是使用不变或持久的数据结构和函数式编程,这样您就不会真正改变任何对象,而可以要求它改变某些状态或改变某些对象,从而倡导使用面向对象的编程。数据,它本身被认为是与对象分离的。容易想象与关系数据库对话的不可变对象。对象本身可以是不可变的,但是它仍然可以更新存储在数据库中的数据。很难想象可以将存储在RAM中的某些数据与对象与数据库中的数据同等地分开,但是实际上并没有太大的区别。

如果有任何需要澄清的地方,请发表评论。



 类似资料:
  • 温馨提示:该项目除了使用 BSD 协议授权外,还需遵守附加的 专利授权。 Immutable 是 Facebook 开发的不可变数据集合。不可变数据一旦创建就不能被修改,使得应用开发更简单,允许使用函数式编程技术,比如惰性评估。Immutable JS 提供一个惰性 Sequence,允许高效的队列方法链,类似 map 和 filter ,不用创建中间代表。 immutable 通过惰性队列和哈希

  • 这里我们再次使用调用,因为我们有对象存储在数组中。 我们调用push来向列表中添加项目,就像我们在数组上调用它一样。 但是因为我们创建了一个新的副本,我们必须重新绑定变量。 当我们想更新特定索引中的项目时,我们有相同的 和 update调用。 我们还可以访问数组函数,如,reduce还支持像我们这里使用的额外方法 。

  • 目录 为什么应该使用 Immutable.JS 等不可变的库? 为什么应该选择 Immutable.JS 作为不可变的库? 使用 Immutable.JS 有什么问题? Immutable.JS 是否值得使用? 在 Redux 中使用 Immutable.JS 有哪些最佳实践? 为什么应该使用 Immutable.JS 等不可变的库? Immutable.JS 不可变的库被设计旨在解决 JavaS