JavaScript中的不可变项和集合

时间:2017-02-15 23:49:46

标签: javascript arrays immutable-collections

我试图了解如何在JavaScript / TypeScript中使用Immutables而不用花一整天的时间。我还没有准备好潜入Immutable.js,因为它似乎会让你保持高度和干燥的类型安全。

所以让我们举一个例子,我有一个数组,其中的元素都是MyType类型。在我的班级中,我有一个搜索数组并返回匹配元素副本的方法,因此我们不会编辑原文。现在说,稍后我需要查看对象是否在数组中,但我所拥有的是副本,而不是原始文件。

处理此问题的标准方法是什么?我可以想到的任何方法来确定我是否已经拥有这个项目将采取某种形式循环遍历集合并访问每个元素然后进行笨重的平等匹配,无论是否将它们都转换为字符串或使用第三方库。

我想使用Immutables,但我一直遇到这样的情况,使它们看起来很不吸引人。我错过了什么?

2 个答案:

答案 0 :(得分:1)

我怀疑我的解决方案不是" ...处理此问题的标准方法。"但是,我认为这至少是 做我认为你要问的方式。

你写道,你有一个方法," ...返回一个匹配元素的副本,所以我们不编辑原文"。您是否可以更改该方法,以便它返回 原始副本?

作为一个例子,下面的策略包括从数组中检索原始元素(以后可以通过引用进行搜索)以及克隆(可以根据需要进行操作而不影响原始元素)。在检索过程中仍然需要克隆原始文件,但至少在以后搜索数组时,不必对数组中的每个元素进行此类转换。此外,它甚至允许您区分按值相同的数组元素,如果您最初只检索元素的副本,则这是不可能的。下面的代码通过使每个数组元素按值相同来展示这一点(但是,根据对象的定义,不同的引用)。

我不知道这是否违反了其他不可变性的最佳做法,例如,保留对元素的引用副本(我认为,即使它们目前没有被违反,我也会将代码保留为未来违反不变性的行为) ......虽然你可以deep-freeze原件以防止未来的突变)。但是它至少允许您在技术上保持一切不变,同时仍然可以通过引用进行搜索。因此,您可以根据需要改变您的克隆,但仍然始终保持原始的相关副本。



const retrieveDerivative = (array, elmtNum) => {
  const orig = array[elmtNum];
  const clone = JSON.parse(JSON.stringify(orig));
  return {orig, clone};
};

const getIndexOfElmt = (array, derivativeOfElement) => {
  return array.indexOf(derivativeOfElement.orig);
};


const obj1 = {a: {b: 1}}; // Object #s are irrelevant.
const obj3 = {a: {b: 1}}; // Note that all objects are identical
const obj5 = {a: {b: 1}}; // by value and thus can only be
const obj8 = {a: {b: 1}}; // differentiated by reference.

const myArr = [obj3, obj5, obj1, obj8];

const derivedFromSomeElmt = retrieveDerivative(myArr, 2);

const indexOfSomeElmt = getIndexOfElmt(myArr, derivedFromSomeElmt);

console.log(indexOfSomeElmt);




答案 1 :(得分:1)

您所描述的情况是可变数据结构具有明显优势的情况,但如果您从使用不可变数据中获益,则有更好的方法。

虽然保持它不可变意味着你的新更新对象是全新的,这会削减两种方式:你可能有一个新对象,但你仍然可以访问原始对象!你可以做很多巧妙的事情,例如:链接您的对象,以便您拥有撤消历史记录,并且可以及时返回以回滚更改。

所以不要在阵列中使用一些hacky查找属性。您的示例的问题是因为您在错误的时间构建了一个新对象:没有函数返回该对象的副本。让函数返回原始对象,并使用原始对象作为索引来调用更新。

let myThings = [new MyType(), new MyType(), new MyType()];

// We update by taking the thing, and replacing with a new one.
// I'll keep the array immutable too
function replaceThing(oldThing, newThing) {
  const oldIndex = myThings.indexOf(oldThing);
  myThings = myThings.slice();
  myThings[oldIndex] = newThing;
  return myThings;
}

// then when I want to update it
// Keep immutable by spreading
const redThing = myThings.find(({ red }) => red);
if (redThing) {
  // In this example, there is a 'clone' method
  replaceThing(redThing, Object.assign(redThing.clone(), {
    newProperty: 'a new value in my immutable!',
  });
}

所有这一切,课程使这一切变得更加复杂。保持简单对象不可变更容易,因为你可以简单地将旧对象传播到新对象中,例如, { ...redThing, newProperty: 'a new value' }。一旦你得到一个高于1个高度的对象,你可能会发现immutable.js更有用,因为你可以mergeDeep