javaScript comes deep copy

By reference calls (call by reference)

Annotation: It may be a little calling around by reference, you as a "call by reference as an argument."

JavaScript deliver all things by reference. If you do not know what it means, then look at this example:

function mutate(obj) {
 obj.a = true;
}
const obj = {a: false};
mutate(obj)
console.log(obj.a); // true
复制代码

Function mutate changed the object passed as a parameter. In the environment "by value call (call by value)", the function will be passed over the value, which is a copy, this function uses this value to work with. Any changes made to the object function are not visible outside the function. But in JavaScript like this environment, the function will get a "call by reference" - you guessed it - a reference , and will change the actual object itself. So last console.log prints out true.

But in some cases, you might want to keep your original object and create a copy of the corresponding function.

Translation: For this is not very understanding friends can look at the following related resources

  • You Don't Know Js -- types&grammer -- cp2 values

  • The difference between primitive types and reference types

Shallow copy: Object.assign ()

One way is to use a copy of an object Object.assign (target, sources ...). He accepts any number of source objects, enumerate all their own properties and those properties are assigned to target. If we use a new, empty object as target, we basically making copies.

obj = const / * ... * /; 
const = Object.assign Copy ({}, obj); 
duplicated code

However, this is a shallow copy. If our object contains an object, they will remain shared references, this 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); // true
复制代码

Another potential is Object.assign () converts getter into a pure property.

Translation: What is the relationship and getter? To try:

var myObject = {
	get say() {
		return 'Hi, xiaohesong';
	}
};
console.log('before assign', Object.getOwnPropertyDescriptor(myObject, 'say'))
var obj = Object.assign({}, myObject)
console.log('after assign', Object.getOwnPropertyDescriptor(obj, 'say'));
复制代码

Try hands, it is not to become mere property? Try it before you start chanting setter?

So now what? It turns out that there are two ways to create an object of deep copy.

Note: The question was raised about the object to expand the operator. In fact, the object is to expand creates a shallow copy.

Translation: On Expanding operator can look at several properties before the introduction of the next.

JSON.parse

There is the oldest method to create a copy of an object is to convert a string representation of the object, and then he resolved back to the object form. This feels a little heavy, but really effective:

obj = const / * ... * /; 
const = Copy the JSON.parse (the JSON.stringify (obj)); 
duplicated code

There is a drawback is that you create a temporary and may be very large string, its purpose is to turn back to the parser. Another disadvantage is that this method can not handle cyclic object. No matter how you think, these are very likely to occur. For example, when building a tree data structure, the parent node reference node and the parent node and its child nodes by reference.

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!
复制代码

In addition, like Maps, Sets, RegExps, Dates, ArrayBuffers and other built-in types such things when serializing lost.

Translation: The JSON.stringify friends do not understand, you can see You Do not Know Js: Types & Grammar cp4: coercion JSON Stringification.

Structured Clone

Cloning is a structured existing algorithms for the value transfer from one domain to another. For example, when you call postMessage send a message to another window (window) or WebWorker, it will use this method. Cloning structured benefit is that it can support a large object and the processing cycle of built-in types. The problem is that, at the time of writing, the algorithm is not directly exposed, only as part of other API. So we have to look at those API, is not it. . .

MessageChannel

As I said, postMessage, will use a structured clone algorithm (translation: when for the object, this is the case) whenever called. We can create a MessageChannel and send a message. At the receiving end, a data message containing the original object of the clone.

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);
复制代码

这种方法的缺点是它是异步的。这也没什么大不了的,但有时你需要以一种同步方式来深度拷贝对象。

History API

如果你曾经使用history.pushState()来构建SPA,那么你就会知道可以提供一个state对象来保存URL。事实证明,这个state对象在结构的克隆上是同步进行的。我们必须小心不要打乱任何可能使用state对象的程序逻辑,所以我们需要在完成克隆后恢复原始state。要防止触发任何事件,请使用history.replaceState()而不是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);
复制代码

再一次,只是为了复制一个对象而进入浏览器的引擎感觉有点重,但是你必须做你必须做的事情。此外,Safari将replaceState的调用数量限制在30秒内100次。

Notification API

Twitter上发了一个推文之后,Jeremy Banks向我展示了第三种方式利用结构化克隆:

function structuralClone(obj) {
 return new Notification('', {data: obj, silent: true}).data;
}
const obj = /* ... */;
const clone = structuralClone(obj);
复制代码

短小,简洁。我喜欢他。不过,它基本上是需要在浏览器中启动权限机制,所以我怀疑它非常慢。由于某种原因,Safari始终为数据对象返回undefined。

性能特征

我想衡量这些方法中哪一种是性能最好的。在我的第一次(天真的)尝试中,我使用了一个小JSON对象,并通过这些不同的方法将其拷贝一千次。幸运的是,Mathias Bynens告诉我V8有一个缓存,用于在对象中添加属性。所以这基本是没啥参考性了。为了确保我没有命中缓存,我编写了一个函数,它使用随机命名生成给定深度和宽度的对象,并重新运行测试。

译: 对于v8有一个缓存,也可以看看在下之前的一个文章外形和内联缓存

图解!

以下是Chrome,Firefox和Edge中不同技术的表现。 越低越好。

javaScript自带的深拷贝


javaScript自带的深拷贝


总结

那么我们从本文得到了什么?

  • 如果你不需要循环对象,也不需要保留内置类型,那么你可以使用JSON.parse(JSON.stringify())在所有浏览器中获得最快的克隆,我发现这非常令人惊讶。

  • 如果你想要一个合适的结构化克隆,MessageChannel是你唯一可靠的跨浏览器选择。

如果我们将structuredClone()作为对应平台上的一个函数会不会更好?我当然这么认为,并重新审视了HTML规范的已存在的一个issue,以重新考虑这种方法。

javaScript自带的深拷贝



Guess you like

Origin blog.51cto.com/14516511/2434508
Recommended