1

我正在尝试为我的第一个 Redux (NGRX/Store) 项目了解 Immutability。避免状态突变被证明是非常痛苦的,在与 Object.assign({}) 和状态突变错误作斗争之后,我发现了 Immutable.JS。这让事情变得容易多了。

假设我有一个金融交易应用程序,它需要在加载时在图表上显示一组条形图。每秒几次,最后一根柱需要根据实时价格信息进行更新,并且每隔一段时间就会添加一个新柱。

对于 {1-n} 种金融工具(EURUSD/GBPJPY/Gold/Oil 等),这一切都需要发生。所以我为我的应用程序的这一部分提出了这个模型:

export interface CandleState {
  LastCompletedCandle : Candle;
  InProgressCandle : Candle;
  LastTick:Offer;
  ClosedCandles:immutable.List<Candle>;
};

export interface AllCandleState {
   instruments: immutable.Map<string, CandleState>
}

您会注意到我有一个包含不可变列表的不可变映射。所以我的第一个问题是:像这样做“不变性中的不变性”有什么意义吗?自从打电话

instruments.set("EURUSD", { [my new state] })

正在有效地返回一个全新的状态,因此我不清楚我是否需​​要像这样具有嵌套不变性......我确实希望能够订阅 ClosedCandles 列表上的更改;使这个不可变会允许事物直接观察变化吗?或者这些东西是否仅从“顶级”级别检测到。

我想我的下一个问题是:我是否需要担心这个。我已经意识到更改不可变集合是一项非常昂贵的操作。如果我这样做 list.push 或 map.set 引擎盖下实际发生了什么。我是否将整个数组中的每一项都复制或映射到一个全新的数组/映射中 - 每次我需要更改不可变集合中的某些内容时?还是我只是在更改参考或其他内容?

我希望有一些关于不可变集合的 Big-Oh Complexity 的已发布信息,这样可以很容易地理解这些东西将如何执行,但我在任何地方都找不到。

4

1 回答 1

1

像这样做“不变性中的不变性”有什么意义吗?

对于大多数部分,非常原始的答案是:不。 - 出于更改检测的目的,对象是否不可变并不重要 - 但是它确实可以帮助您强制执行始终创建新对象并且从不改变现有状态的概念在商店之外(甚至在减速器本身)。

我确实希望能够订阅 ClosedCandles 列表上的更改;使这个不可变会允许事物直接观察变化吗?或者这些东西是否仅从“顶级”级别检测到。

是和不是。直接观察变化可以通过设置一个直接选择的流来实现instruments.ClosedCandles- 但是这不是不变性的特殊情况,这可以在有或没有不变性的情况下完成。

现在对于不变性和自上而下的部分:总是这样,当你想改变一个对象的 n 级时,你改变的对象的每个父级(直到根)都必须重新创建,简单地说因为如果不可变,则无法更改父级,因此您不能只设置新的引用。

例子:

root                <-- to enable changing the map1, you have to recreate the root, since it is immutable
|--map1             <-- to enable changing the set2, you have to recreate this map, since it is immutable
|  |--set1          <-- untouched, the "new version" of map1 will reference the same "set1"
|  \--set2          <-- to enable changing the attribute2, you have to recreate this set, since it is also immutable
|     |--attribute1 <-- untouched, the "new version" of set2 will just have the same reference on this object as the "old version"
|     \--attribute2 <-- you want to alter this attribute
|
|--map2             <-- untouched, the "new version" of root will reference the same "map2"
\--map3             <-- untouched, the "new version" of root will reference the same "map3"

我想我的下一个问题是:我是否需要担心这个。我已经意识到更改不可变集合是一项非常昂贵的操作。如果我这样做 list.push 或 map.set 引擎盖下实际发生了什么。我是否将整个数组中的每一项都复制或映射到一个全新的数组/映射中 - 每次我需要更改不可变集合中的某些内容时?还是我只是在更改参考或其他内容?

这主要取决于您如何使用不可变对象,大多数库对此进行了优化,并且还包装了这些东西,因此您不必担心。至于性能:在大多数情况下,这没什么大不了的,大多数突变只改变状态的一小部分,而状态的其余部分将只包含新的引用,通常不会创建新的对象。

但是,在性能关键的情况下,您可以使用一种策略,仅将不可变对象用于开发和测试构建以确保您的应用程序正常工作,而对于生产构建,您可以停用不可变性以优化最后一点性能。

于 2016-11-24T13:17:37.903 回答