返回

数据不变,对象可改,优雅的 Immer

前端

不可变数据是只读数据,一旦创建就不能再被改变。这与可变数据相反,可变数据可以被修改或更新。

追求不可变数据的主要原因是它可以帮助我们编写更可靠、更易于维护的代码。当我们使用不可变数据时,我们可以确信数据不会在我们的代码中意外更改。这可以帮助我们避免错误并使我们的代码更易于理解。

实现不可变数据

实现不可变数据的一种方法是使用对象冻结(Object.freeze())。然而,这只能冻结对象本身,而不能冻结对象中的属性。为了冻结对象中的所有属性,我们需要使用递归冻结(deep freeze)。

function deepFreeze(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  Object.freeze(obj);

  for (const key of Object.keys(obj)) {
    deepFreeze(obj[key]);
  }

  return obj;
}

另一种实现不可变数据的方法是使用代理(proxy)。代理是一个对象,它可以拦截对另一个对象的访问并修改对该对象的访问。我们可以使用代理来创建一个不可变的视图,它可以使我们对对象进行修改而不改变对象本身。

const obj = {
  foo: 'bar',
  baz: {
    qux: 'quux'
  }
};

const proxy = new Proxy(obj, {
  set: function(target, property, value) {
    throw new Error('Cannot set property ' + property + ' on immutable object');
  }
});

proxy.foo = 'quux'; // Error: Cannot set property foo on immutable object

Immer

Immer 是一个 JavaScript 库,它提供了一种简单的方法来创建可变对象而不破坏不可变性。它使用代理来创建一个不可变的视图,它可以使我们对对象进行修改而不改变对象本身。

const obj = {
  foo: 'bar',
  baz: {
    qux: 'quux'
  }
};

const nextObj = produce(obj, draft => {
  draft.foo = 'quux';
  draft.baz.qux = 'corge';
});

console.log(obj); // { foo: 'bar', baz: { qux: 'quux' } }
console.log(nextObj); // { foo: 'quux', baz: { qux: 'corge' } }

在上面的示例中,我们使用 Immer 的 produce() 函数来创建一个不可变的视图。然后,我们可以在视图中对对象进行修改,而不会改变对象本身。

结论

Immer.js 是一个强大的库,它可以帮助我们编写更可靠、更易于维护的代码。通过使用 Immer.js,我们可以轻松地创建可变对象而不破坏不可变性。