iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💭

What Is the Best Way to Copy and Edit Objects?

に公開

Conclusion: Copying with structuredClone and then editing the object seems to be well-balanced from a performance perspective.

What I wanted to verify

I wanted to investigate which method is the fastest when you want to copy an object like the following and change a property in a deep hierarchy.

const obj = {
  prop: { sub: { val: '' } },
  prop2: { sub: { val: '' } },
};

Here are the candidates for the copying method:

  • Manually using the spread operator
  • Using structuredClone
  • Using immer
  • JSON.parse(JSON.stringify(obj)) [1]

Note that in this case, we are not deep-copying everything; it is acceptable to use shallow copies for parts that do not change. In other words, prop2 can be a shallow copy. This is because I encountered such a case in actual work.

HTML file used for verification (verbatim)
<!DOCTYPE html>

<script type="module">
  import { produce } from 'https://unpkg.com/immer@10.0.2/dist/immer.production.mjs';
  const obj = {
    prop: { sub: { val: '' } },
    prop2: { sub: { val: '' } },
  };

  console.time('raw');
  for (let i = 0; i < 1e5; i++) {
    const copied = {
      ...obj,
      prop: { ...obj.prop, sub: { ...obj.prop.sub, val: `${i}_dayo` } },
    };
  }
  console.timeEnd('raw');

  console.time('structuredClone');
  for (let i = 0; i < 1e5; i++) {
    const copied = structuredClone(obj);
    copied.prop.sub.val = `${i}_dayo`;
  }
  console.timeEnd('structuredClone');

  console.time('immer');
  for (let i = 0; i < 1e5; i++) {
    const copied = produce(obj, (draft) => {
      draft.prop.sub.val = `${i}_dayo`;
    });
  }
  console.timeEnd('immer');

  console.time('JSON.stringify');
  for (let i = 0; i < 1e5; i++) {
    const copied = JSON.parse(JSON.stringify(obj));
    copied.prop.sub.val = `${i}_dayo`;
  }
  console.timeEnd('JSON.stringify');
</script>

Results

Measurements were taken on Chrome for macOS. The results were as follows.

Method Time
raw 8.72290039062 ms
JSON.stringify 47.80322265625 ms
structuredClone 116.84326171875 ms
immer 152.74682617875 ms

Increasing the number of properties slightly resulted in this.

Method Time
raw 11.43896484 ms
JSON.stringify 277.97060546 ms
immer 279.44091796 ms
structuredClone 343.45703125 ms

Analysis

Regardless of the object size, the method of manually copying with the spread operator seemed to be the fastest.
...I'd like to say that, but the requirement was that the added properties could be shallow-copied. This might be why only "raw" didn't see much change in speed.

Methods other than the spread operator seem to depend heavily on the size of the object. However, the other three methods were almost in the same order of magnitude, so which one to use seems to be a matter of preference.

Conclusion: structuredClone seems to be well-balanced

While I've discussed various points, I think it is best to use structuredClone as long as it doesn't become a performance bottleneck. The code is simpler, and the performance is not as bad as one might think. Almost all modern browsers support structuredClone. It seems like a very well-balanced and solid choice.

脚注
  1. This method should not really be used as it has issues such as not being able to preserve Date types. For example, if you try JSON.parse(JSON.stringify(new Date())), the result is a string value. ↩︎

Discussion