WEB程序员笔记

一个前端开发工程师的个人博客

使用代理对象滚动自己的JavaScript不变性函数

尽管JavaScript允许我们更改对象,但我们可能选择不允许自己(和其他程序员)这样做。在当今的JavaScript世界中,最好的例子之一就是在React应用程序中设置状态。如果我们更改当前状态而不是更改当前状态的新副本,则可能会遇到难以诊断的问题。

在这篇文章中,我们滚动了我们自己的不可变代理函数,以防止对象突变!

什么是对象突变?

作为快速回顾,对象突变是指我们更改对象或数组的属性时。这与重新分配完全不同,在重新分配中,我们完全指向了不同的对象引用。以下是突变与重新分配的几个示例:

// Mutation
const person = { name: "Bo" };
person.name = "Jack";

// Reassignment
let pet = { name: "Daffodil", type: "dog" };
pet = { name: "Whiskers", type: "cat" };

我们必须记住,这也适用于数组:

// Mutation
const people = ["Jack", "Jill", "Bob", "Jane"];
people[1] = "Beverly";

// Reassignment
let pets = ["Daffodil", "Whiskers", "Ladybird"];
pets = ["Mousse", "Biscuit"];

对象突变的意外后果的一个例子

既然我们已经了解了什么是突变,那么突变如何产生意想不到的后果?让我们看下面的例子。

const person = { name: "Bo" };
const otherPerson = person;
otherPerson.name = "Finn";

console.log(person);
// { name: "Finn" }

kes,没错!双方personotherPerson正在引用同一个对象,因此,如果我们发生变异nameotherPerson,这种变化将当我们访问体现person

与其让我们自己(以及我们项目中的其他开发人员)突变这样的对象,不如我们抛出一个错误该怎么办?那就是我们的不可变代理解决方案出现的地方。

我们的不可变代理解决方案

JavaScript Proxy对象是我们可以使用的一些元编程。它允许我们使用自定义功能包装该对象,以实现该对象上的getter和setter之类的功能。

对于我们的不可变代理,让我们创建一个函数,该函数接受一个对象并返回该对象的新代理。当我们尝试get对该对象设置属性时,我们会检查该属性是否是对象本身。如果是这样,那么我们以递归的方式返回包裹在不可变代理中的该属性。否则,我们只返回该属性。

当我们尝试set代理对象的值时,简单地抛出一个错误,让用户知道他们不能set在该对象上设置属性。

这是我们不可变的代理功能:

const person = {
  name: "Bo",
  animals: [{ type: "dog", name: "Daffodil" }],
};

const immutable = (obj) =>
  new Proxy(obj, {
    get(target, prop) {
      return typeof target[prop] === "object"
        ? immutable(target[prop])
        : target[prop];
    },
    set() {
      throw new Error("Immutable!");
    },
  });

const immutablePerson = immutable(person);

const immutableDog = immutablePerson.animals[0];

immutableDog.type = "cat";
// Error: Immutable!

有了它:我们无法对不可变对象的属性进行突变!

我应该在生产中使用它吗

不,可能不会。这种练习在学术上非常棒,但是有各种各样的棒,健壮且经过测试的解决方案可以完成相同的工作(例如ImmutableJS和ImmerJS)。如果您希望在应用程序中包含不可变的数据结构,我建议您查看这些很棒的库!

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注