Home > Web Front-end > JS Tutorial > body text

Deep understanding of JavaScript deep copy performance

小云云
Release: 2018-02-03 14:28:23
Original
2056 people have browsed it

This article mainly shares with you the analysis of JavaScript deep copy performance. How to copy an object in JavaScript? This is a very simple question, but the answer is not simple.

If you don't know what it means, take a look at the following example:

function mutate(obj) {
  obj.a = true;
}

const obj = {a: false};
mutate(obj)
console.log(obj.a); // 输出 true
Copy after login

Function mutate changes its parameters. In the value-passing scenario, the formal parameter of the function is just a copy of the actual parameter - a copy - and the actual parameter is not changed when the function call is completed. But in JavaScript's pass-by-reference scenario, the formal parameters and actual parameters of the function point to the same object. When the formal parameters are changed inside the parameters, the actual parameters outside the function are also changed.

So in some cases, you need to retain the original object. In this case, you need to pass a copy of the original object into the function to prevent the function from changing the original object.

Shallow copy: Object.assign()

A simple way to obtain a copy of an object is to use Object.assign(target, sources...) . It accepts any number of source objects, enumerates all their properties and assigns them to target. If we use a new empty object target, then we can copy the object.

const obj = /* ... */;
const copy = Object.assign({}, obj);
Copy after login

However this is just a shallow copy. If our objects contain other objects as their properties, they will maintain shared references, which is not what we want:

function mutateDeepObject(obj) {
  obj.a.thing = true;
}

const obj = {a: {thing: false}};
const copy = Object.assign({}, obj);
mutateDeepObject(copy)
console.log(obj.a.thing); // prints true
Copy after login
Object.assign Method will only copy the source object Own and enumerable properties to the target object. This method uses the [[Get]] of the source object and the [[Set]] of the target object, so it calls the relevant getter and setter . Therefore, it assigns properties rather than just copying or defining new properties. If the merge source contains getter, this may make it unsuitable for merging new properties into the prototype. In order to copy a property definition (including its enumerability) to the prototype, Object.getOwnPropertyDescriptor() and Object.defineProperty() should be used.

So what to do now? There are several ways to create a deep copy of an object.

Note: Maybe someone mentioned the object destructuring operation, which is also a shallow copy.

JSON.parse

One of the oldest ways to create a copy of an object is to convert that object to its JSON string representation, Then parse it back into an object. This feels a bit oppressive, but it works:

const obj = /* ... */;
const copy = JSON.parse(JSON.stringify(obj));
Copy after login

The disadvantage here is that you create a temporary, potentially large string just to put it back into the parser. Another disadvantage is that this method cannot handle cyclic objects. And looping objects happens often. For example, when you build a tree-like data structure, a node refers to its parent, which in turn refers to its children.

const x = {};
const y = {x};
x.y = y; // Cycle: x.y.x.y.x.y.x.y.x...
const copy = JSON.parse(JSON.stringify(x)); // throws!
Copy after login

In addition, such as Map, Set, RegExp, Date, ArrayBuffer and Other built-in types are lost when serialized.

Structured Clone Structured Cloning Algorithm

Structured cloning is an existing algorithm for transferring values ​​from one place to another. For example, it is used whenever you call postMessage to send a message to another window or WebWorker. The nice thing about structured cloning is that it handles cyclic objects and supports a large number of built-in types. The problem is, at the time of writing this article, the algorithm is not available directly, only as part of other APIs. I guess we should know what's included, shouldn't we. . .

MessageChannel

As I said, the structured cloning algorithm works as long as you call postMessage. We can create a MessageChannel and send messages. On the receiving end, the message contains a structured clone of our original data object.

function structuralClone(obj) {
  return new Promise(resolve => {
    const {port1, port2} = new MessageChannel();
    port2.onmessage = ev => resolve(ev.data);
    port1.postMessage(obj);
  });
}

const obj = /* ... */;
const clone = await structuralClone(obj);
Copy after login

The disadvantage of this method is that it is asynchronous. Although this is not a big deal, sometimes you need to use a synchronous method to deep copy an object.

History API

If you have ever written a SPA using history.pushState(), you know that you can provide a state object to save the URL. It turns out that this state object uses structured cloning - and it's synchronous. We must be careful not to mess up the state objects used by the program logic, so we need to restore the original state after completing the cloning. To prevent any surprises, use history.replaceState() instead of history.pushState().

function structuralClone(obj) {
  const oldState = history.state;
  history.replaceState(obj, document.title);
  const copy = history.state;
  history.replaceState(oldState, document.title);
  return copy;
}

const obj = /* ... */;
const clone = structuralClone(obj);
Copy after login

However, using the browser's engine just to copy an object feels a bit excessive. Additionally, Safari browser limits the number of replaceState calls to 100 times in 30 seconds.

Notification API

After a tweet, Jeremy Banks showed me a third way to leverage structured cloning: the Notification API.

function structuralClone(obj) {
  return new Notification('', {data: obj, silent: true}).data;
}

const obj = /* ... */;
const clone = structuralClone(obj);
Copy after login

Short and concise. I like it!

However, it requires the permission mechanism inside the browser, so I suspect it is very slow. For some reason, Safari always returns undefined.

Performance extravaganza

I want to measure which method is the most performant. In my first (naive) attempt, I took a small JSON object and cloned the object a thousand times in different ways. Fortunately, Mathias Bynens told me that V8 has a cache when you add properties to an object. So I'm benchmarking the cache. To make sure I never hit the cache, I wrote a function that generates an object of a given depth and width with a random key name and reran the test.

chart!

Here are the performance of different technologies in Chrome, Firefox and Edge. The lower the better.

Deep understanding of JavaScript deep copy performance

Deep understanding of JavaScript deep copy performance

Deep understanding of JavaScript deep copy performance

#CONCLUSION

So what do we get from this?

  • If you are not looping over objects and do not need to preserve built-in types, you can use the cross-browser

    JSON.parse(JSON.stringify()) to get the most Fast cloning performance, which really surprised me.

  • If you want a properly structured clone,

    MessageChannel is your only reliable cross-browser choice.

Would it be better if the browser platform directly provided a

structuredClone() function? I certainly think so, the latest HTML specification is discussing this Synchronous clone = global.structuredClone(value, transfer = []) API · Issue #793 · whatwg/html.

Related recommendations:


JQuery’s $.extend shallow copy and deep copy example analysis

Achieve deep copy in jquery Copy and shallow copy

What are shallow and deep copies in Js

The above is the detailed content of Deep understanding of JavaScript deep copy performance. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template