为什么我应该使用 immutablejs 而不是 object.freeze?
Why should I use immutablejs over object.freeze?
我在网上研究了 immutablejs 相对于 Object.freeze()
的好处,但没有发现任何令人满意的东西!
我的问题是,当我可以冻结一个普通的旧 javascript 对象时,为什么我应该使用这个库并使用非本机数据结构?
Object.freeze
本身不做任何深度冻结,我相信 immutable.js 做。
与任何库都一样——为什么要使用下划线、jquery 等等
人们喜欢重复使用别人制造的轮子:-)
想到的最大原因 - 除了具有有助于不可变更新的功能 api 之外,是 Immutable.js 使用的结构共享。如果您的应用程序需要强制不变性(即,您正在使用 Redux),那么如果您仅使用 Object.freeze,那么您将为每个 'mutation' 创建一个副本。随着时间的推移,这并不是很有效,因为这会导致 GC 抖动。使用 Immutable.js,您可以获得结构共享(而不是必须实现您自己的对象 pool/a 结构共享模型),因为从不可变返回的数据结构是 Tries。这意味着所有的变化仍然在数据结构中被引用,因此 GC 抖动保持在最低限度。有关更多信息,请访问 Immutable.js 的文档站点(以及由创作者 Lee Byron 制作的更深入的精彩视频):
它们都没有使对象深度不可变。
但是,使用 Object.freeze
您必须自己创建对象/数组的新实例,并且它们没有结构共享。因此,每一次更改都需要深度复制所有内容,而旧集合将被垃圾收集。
另一方面,immutablejs
将管理集合,当某些内容发生变化时,新实例将使用旧实例中未发生变化的部分,从而减少复制和垃圾收集。
我认为您不了解 immutablejs 提供的功能。它不是一个让你的对象不可变的库,它是一个处理不可变值的库。
在不简单地重复他们的 docs and mission statement 的情况下,我将说明它提供的两件事:
类型。他们实现了(不可变的)无限范围、堆栈、有序集、列表……
它们的所有类型都实现为Persistent Data Structures。
我撒谎了,这里引用他们的使命宣言:
Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.
我强烈建议您阅读他们 link 的文章和视频,以及更多关于持久数据结构的内容(因为它们是 immutablejs 的相关内容),但是我用一句话总结一下:
假设您正在编写一个游戏,并且您有一个坐在二维平面上的玩家。例如,这里是鲍勃:
var player = {
name: 'Bob',
favouriteColor: 'moldy mustard',
x: 4,
y: 10
};
自从你喝了 FP koolaid 后你想冻结玩家(brrr!希望 Bob 得到一件毛衣):
var player = Object.freeze({
name: 'Bob',
...
});
现在进入你的游戏循环。在每一次滴答声中,玩家的位置都会改变。我们不能只更新播放器对象,因为它被冻结了,所以我们将其复制过来:
function movePlayer(player, newX, newY) {
return Object.freeze(Object.assign({}, player, { x: newX, y: newY }));
}
这很好,很花哨,但请注意我们进行了多少无用的复制:每次更新时,我们都会创建一个新对象,遍历我们的一个对象,然后在它们之上分配一些新值。在每一个刻度上,在你的每一个物体上。真是一口。
Immutable 为您总结:
var player = Immutable.Map({
name: 'Bob',
...
});
function movePlayer(player, newX, newY) {
return player.set('x', newX).set('y', newY);
}
并且通过 ノ*✧゚ 魔法 ✧゚*ヽ 持久化数据结构,他们承诺尽可能执行 最少 的操作。
还有心态的差异。使用 "a plain old [frozen] javascript object" 时,everything 的默认操作是假定可变性,并且您必须付出额外的努力才能实现有意义的不变性(也就是说不变性承认该状态存在)。这就是 freeze
存在的部分原因:当您尝试不这样做时,事情就会变得混乱。对于 Immutablejs,不变性当然是默认假设,并且它上面有一个很好的 API。
这并不是说一切都是粉红色和玫瑰色,顶部是樱桃色。当然,任何事情都有它的缺点,你不应该仅仅因为你可以就到处塞进 Immutable。有时,只需 freeze
ing 一个对象就足够了。哎呀,大多数时候 已经绰绰有余了。这是一个有用的库,有它的利基,只是不要被炒作冲昏头脑。
根据我的 benchmarks,immutable.js 针对写入操作进行了优化 ,比 Object.assign() 更快,但是速度更慢用于读取操作。因此,决定取决于您的应用程序类型及其 read/write 比率。以下是基准测试结果的摘要:
-- Mutable
Total elapsed = 103 ms = 50 ms (read) + 53 ms (write).
-- Immutable (Object.assign)
Total elapsed = 2199 ms = 50 ms (read) + 2149 ms (write).
-- Immutable (immutable.js)
Total elapsed = 1690 ms = 638 ms (read) + 1052 ms (write).
-- Immutable (seamless-immutable)
Total elapsed = 91333 ms = 31 ms (read) + 91302 ms (write).
-- Immutable (immutable-assign (created by me))
Total elapsed = 2223 ms = 50 ms (read) + 2173 ms (write).
理想情况下,您应该在引入任何性能优化之前分析您的应用程序,但是,不变性是必须尽早决定的设计决策之一。当您开始使用 immutable.js 时,您需要在整个应用程序中使用它以获得性能优势,因为使用 fromJS() 和 toJS() 与普通 JS 对象的互操作非常昂贵。
PS: 刚刚发现深度冻结数组(1000 个元素)的更新速度变得非常慢,大约慢了 50 倍,因此您应该只在开发模式下使用深度冻结。基准测试结果:
-- Immutable (Object.assign) + deep freeze
Total elapsed = 45903 ms = 96 ms (read) + 45807 ms (write).
Object.freeze() 和 immutable.js 之间有几个主要区别。
让我们先解决性能成本问题。 Object.freeze() 很浅。它将使对象不可变,但所述对象内的嵌套属性和方法仍然可以改变。 Object.freeze() documentation 解决了这个问题,甚至继续提供 "deepFreeze" 函数,这在性能方面成本更高。另一方面,Immutable.js 将使对象作为一个整体(嵌套属性、方法等)以较低的成本保持不变。
此外,如果您需要克隆不可变变量,Object.freeze() 将强制您创建一个全新的变量,而 Immutable.js 可以重用现有的不可变变量以更有效地创建克隆.这是来自 this article 的一段有趣的话:
"Immutable methods like .set() can be more efficient than cloning
because they let the new object reference data in the old object: only
the changed properties differ. This way you can save memory and
performance versus constantly deep-cloning everything."
简而言之,Immutable.js 在新旧不可变变量之间建立了逻辑联系,从而提高了克隆的性能,space 冻结的变量占用了内存。 Object.freeze() 可悲的是没有 - 每次你从一个冻结的对象中克隆一个新变量时,你基本上都会重新写入所有数据,并且两个不可变变量之间没有逻辑联系,即使(出于某种奇怪的原因)它们保存相同的数据。
所以在性能方面,特别是如果你经常在程序中使用不可变变量,Immutable.js 是一个很好的选择。然而,性能并不是一切,使用 Immutable.js 有一些重要的注意事项。 Immutable.js 使用它自己的数据结构,这使得调试,甚至只是将数据记录到控制台,都非常痛苦。它还可能导致基本 JavaScript 功能的丢失(例如,you cannot use ES6 de-structuring with it) Immutable.js 文档是出了名的无法理解(因为它最初是为仅在 Facebook 内部使用而编写的) , 即使出现简单的问题也需要大量的网络搜索。
我希望这涵盖了这两种方法最重要的方面,并帮助您决定哪种方法最适合您。
我在网上研究了 immutablejs 相对于 Object.freeze()
的好处,但没有发现任何令人满意的东西!
我的问题是,当我可以冻结一个普通的旧 javascript 对象时,为什么我应该使用这个库并使用非本机数据结构?
Object.freeze
本身不做任何深度冻结,我相信 immutable.js 做。
与任何库都一样——为什么要使用下划线、jquery 等等
人们喜欢重复使用别人制造的轮子:-)
想到的最大原因 - 除了具有有助于不可变更新的功能 api 之外,是 Immutable.js 使用的结构共享。如果您的应用程序需要强制不变性(即,您正在使用 Redux),那么如果您仅使用 Object.freeze,那么您将为每个 'mutation' 创建一个副本。随着时间的推移,这并不是很有效,因为这会导致 GC 抖动。使用 Immutable.js,您可以获得结构共享(而不是必须实现您自己的对象 pool/a 结构共享模型),因为从不可变返回的数据结构是 Tries。这意味着所有的变化仍然在数据结构中被引用,因此 GC 抖动保持在最低限度。有关更多信息,请访问 Immutable.js 的文档站点(以及由创作者 Lee Byron 制作的更深入的精彩视频):
它们都没有使对象深度不可变。
但是,使用 Object.freeze
您必须自己创建对象/数组的新实例,并且它们没有结构共享。因此,每一次更改都需要深度复制所有内容,而旧集合将被垃圾收集。
immutablejs
将管理集合,当某些内容发生变化时,新实例将使用旧实例中未发生变化的部分,从而减少复制和垃圾收集。
我认为您不了解 immutablejs 提供的功能。它不是一个让你的对象不可变的库,它是一个处理不可变值的库。
在不简单地重复他们的 docs and mission statement 的情况下,我将说明它提供的两件事:
类型。他们实现了(不可变的)无限范围、堆栈、有序集、列表……
它们的所有类型都实现为Persistent Data Structures。
我撒谎了,这里引用他们的使命宣言:
Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.
我强烈建议您阅读他们 link 的文章和视频,以及更多关于持久数据结构的内容(因为它们是 immutablejs 的相关内容),但是我用一句话总结一下:
假设您正在编写一个游戏,并且您有一个坐在二维平面上的玩家。例如,这里是鲍勃:
var player = {
name: 'Bob',
favouriteColor: 'moldy mustard',
x: 4,
y: 10
};
自从你喝了 FP koolaid 后你想冻结玩家(brrr!希望 Bob 得到一件毛衣):
var player = Object.freeze({
name: 'Bob',
...
});
现在进入你的游戏循环。在每一次滴答声中,玩家的位置都会改变。我们不能只更新播放器对象,因为它被冻结了,所以我们将其复制过来:
function movePlayer(player, newX, newY) {
return Object.freeze(Object.assign({}, player, { x: newX, y: newY }));
}
这很好,很花哨,但请注意我们进行了多少无用的复制:每次更新时,我们都会创建一个新对象,遍历我们的一个对象,然后在它们之上分配一些新值。在每一个刻度上,在你的每一个物体上。真是一口。
Immutable 为您总结:
var player = Immutable.Map({
name: 'Bob',
...
});
function movePlayer(player, newX, newY) {
return player.set('x', newX).set('y', newY);
}
并且通过 ノ*✧゚ 魔法 ✧゚*ヽ 持久化数据结构,他们承诺尽可能执行 最少 的操作。
还有心态的差异。使用 "a plain old [frozen] javascript object" 时,everything 的默认操作是假定可变性,并且您必须付出额外的努力才能实现有意义的不变性(也就是说不变性承认该状态存在)。这就是 freeze
存在的部分原因:当您尝试不这样做时,事情就会变得混乱。对于 Immutablejs,不变性当然是默认假设,并且它上面有一个很好的 API。
这并不是说一切都是粉红色和玫瑰色,顶部是樱桃色。当然,任何事情都有它的缺点,你不应该仅仅因为你可以就到处塞进 Immutable。有时,只需 freeze
ing 一个对象就足够了。哎呀,大多数时候 已经绰绰有余了。这是一个有用的库,有它的利基,只是不要被炒作冲昏头脑。
根据我的 benchmarks,immutable.js 针对写入操作进行了优化 ,比 Object.assign() 更快,但是速度更慢用于读取操作。因此,决定取决于您的应用程序类型及其 read/write 比率。以下是基准测试结果的摘要:
-- Mutable
Total elapsed = 103 ms = 50 ms (read) + 53 ms (write).
-- Immutable (Object.assign)
Total elapsed = 2199 ms = 50 ms (read) + 2149 ms (write).
-- Immutable (immutable.js)
Total elapsed = 1690 ms = 638 ms (read) + 1052 ms (write).
-- Immutable (seamless-immutable)
Total elapsed = 91333 ms = 31 ms (read) + 91302 ms (write).
-- Immutable (immutable-assign (created by me))
Total elapsed = 2223 ms = 50 ms (read) + 2173 ms (write).
理想情况下,您应该在引入任何性能优化之前分析您的应用程序,但是,不变性是必须尽早决定的设计决策之一。当您开始使用 immutable.js 时,您需要在整个应用程序中使用它以获得性能优势,因为使用 fromJS() 和 toJS() 与普通 JS 对象的互操作非常昂贵。
PS: 刚刚发现深度冻结数组(1000 个元素)的更新速度变得非常慢,大约慢了 50 倍,因此您应该只在开发模式下使用深度冻结。基准测试结果:
-- Immutable (Object.assign) + deep freeze
Total elapsed = 45903 ms = 96 ms (read) + 45807 ms (write).
Object.freeze() 和 immutable.js 之间有几个主要区别。
让我们先解决性能成本问题。 Object.freeze() 很浅。它将使对象不可变,但所述对象内的嵌套属性和方法仍然可以改变。 Object.freeze() documentation 解决了这个问题,甚至继续提供 "deepFreeze" 函数,这在性能方面成本更高。另一方面,Immutable.js 将使对象作为一个整体(嵌套属性、方法等)以较低的成本保持不变。
此外,如果您需要克隆不可变变量,Object.freeze() 将强制您创建一个全新的变量,而 Immutable.js 可以重用现有的不可变变量以更有效地创建克隆.这是来自 this article 的一段有趣的话:
"Immutable methods like .set() can be more efficient than cloning because they let the new object reference data in the old object: only the changed properties differ. This way you can save memory and performance versus constantly deep-cloning everything."
简而言之,Immutable.js 在新旧不可变变量之间建立了逻辑联系,从而提高了克隆的性能,space 冻结的变量占用了内存。 Object.freeze() 可悲的是没有 - 每次你从一个冻结的对象中克隆一个新变量时,你基本上都会重新写入所有数据,并且两个不可变变量之间没有逻辑联系,即使(出于某种奇怪的原因)它们保存相同的数据。
所以在性能方面,特别是如果你经常在程序中使用不可变变量,Immutable.js 是一个很好的选择。然而,性能并不是一切,使用 Immutable.js 有一些重要的注意事项。 Immutable.js 使用它自己的数据结构,这使得调试,甚至只是将数据记录到控制台,都非常痛苦。它还可能导致基本 JavaScript 功能的丢失(例如,you cannot use ES6 de-structuring with it) Immutable.js 文档是出了名的无法理解(因为它最初是为仅在 Facebook 内部使用而编写的) , 即使出现简单的问题也需要大量的网络搜索。
我希望这涵盖了这两种方法最重要的方面,并帮助您决定哪种方法最适合您。