Translator: Crazy Technology House
Original text: https://smalldata.tech/blog/2...
Wechat Public Number: Front-end Pioneer
Welcome to pay attention and push you fresh front-end technology articles every day.
Before I begin, I will popularize some basic knowledge. Javascript objects are just pointers to a location in memory. These pointers are variable, that is, they can be reassigned. So just copying this pointer results in two pointers pointing to the same address in memory.
var foo = { a : "abc" } console.log(foo.a); // abc var bar = foo; console.log(bar.a); // abc foo.a = "yo foo"; console.log(foo.a); // yo foo console.log(bar.a); // yo foo bar.a = "whatup bar?"; console.log(foo.a); // whatup bar? console.log(bar.a); // whatup bar?
As you can see from the example above, both foo and bar can change with each other. So when copying the object in Javascript, we should consider it according to the actual situation.
shallow copy
If all the attributes of the object to be operated on are value types, you can use extended grammar or Object.assign(...)
var obj = { foo: "foo", bar: "bar" }; var copy = { ...obj }; // Object { foo: "foo", bar: "bar" } var obj = { foo: "foo", bar: "bar" }; var copy = Object.assign({}, obj); // Object { foo: "foo", bar: "bar" }
You can see that both methods can copy attributes from multiple different source objects into one target object.
var obj1 = { foo: "foo" }; var obj2 = { bar: "bar" }; var copySpread = { ...obj1, ...obj2 }; // Object { foo: "foo", bar: "bar" } var copyAssign = Object.assign({}, obj1, obj2); // Object { foo: "foo", bar: "bar" }
The above method is problematic. If the object's attributes are also objects, only those pointers are actually copied, which is the same effect as var bar = foo; as in the first code.
var foo = { a: 0 , b: { c: 0 } }; var copy = { ...foo }; copy.a = 1; copy.b.c = 2; console.dir(foo); // { a: 0, b: { c: 2 } } console.dir(copy); // { a: 1, b: { c: 2 } }
Deep copy (limited)
One possible way to make a deep copy of an object is to serialize the object into a string and then deserialize it.
var obj = { a: 0, b: { c: 0 } }; var copy = JSON.parse(JSON.stringify(obj));
Unfortunately, this method only works if the object contains serializable values without circular references. Common things that can't be serialized are date objects -- although they display a stringed ISO date format, JSON.parse only parses it into a string, not a date type.
Deep copy (less restrictive)
For some more complex scenarios, we can use a name provided by HTML5 Structural Cloning New algorithm. However, as of the publication of this article, some built-in types are still not supported, but compared with JSON.parse, it supports more types: Date, RegExp, Map, Set, Blob, FileList, ImageData, sparse and typed Array. It also maintains references to cloned objects, which enables it to support copies of circular reference structures that are not supported in the previously mentioned serialization.
There is no direct call to structured cloning yet, but some new browser features use this algorithm at the bottom. So deep-copy objects may depend on a series of environments.
Via Message Channels: The principle is to borrow the serialization algorithm used in communication. Because it is event-based, cloning here is also an asynchronous operation.
class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } cloneAsync(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredCloneAsync = window.structuredCloneAsync = StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner); const main = async () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = await structuredCloneAsync(original); // different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; main();
Via the history API: history.pushState() and history.replaceState() will make a structured clone of their first parameter! It should be noted that this method is synchronous, because the operation speed of browser history is not very fast, if this method is frequently called, it will lead to browser card death.
const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; };
Via notification API When a notification instance is created, the constructor makes a structured clone of its related data. It should be noted that it will try to display browser notifications to users, but it will not do anything unless it receives a request for user permission to display notifications. Once the user clicks on the consent, notification will be closed immediately.
const structuredClone = obj => { const n = new Notification("", {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; };
Deep copy with Node.js
Version 8.0.0 of Node.js provides one Serialized api It is comparable to structured cloning. However, at the time of publication of this article, this API was only marked as experimental:
const v8 = require('v8'); const buf = v8.serialize({a: 'foo', b: new Date()}); const cloned = v8.deserialize(buf); cloned.b.getMonth();
The cloneDeep function of lodash can be considered as a stable method under version 8.0.0, and its idea is more or less based on structured cloning algorithm.
conclusion
The best algorithm for object copy in Javascript depends largely on the environment in which it is used and the type of object you need to copy. Although lodash is the safest generic deep copy function, if you encapsulate it yourself, you may be able to achieve a more efficient implementation. Here is a simple deep copy, which is also applicable to Date date objects:
function deepClone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = deepClone(obj[i]); } return copy; } // Handle Function if (obj instanceof Function) { copy = function() { return obj.apply(this, arguments); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]); } return copy; } throw new Error("Unable to copy obj as type isn't supported " + obj.constructor.name); }
I'm looking forward to the day when structured cloning can be used casually so that object copying is no longer a headache.^^
Wechat Public Number: Front-end Pioneer
Welcome to scan the two-dimensional code, pay attention to the public number, and push you fresh front-end technical articles every day.
Welcome to continue reading other highly praised articles in this column:
- Twelve amazing CSS pilot projects
- Fifty React Interview Questions Must Be Meet
- What do top companies in the world ask in front-end interviews?
- 11 Best JavaScript Dynamic Effect Libraries
- CSS Flexbox Visualization Manual
- React from the Designer's Perspective
- Is the festival boring? Write a brain game in JavaScript.
- How does CSS sticky positioning work
- Step by step teach you how to use HTML5 SVG to achieve animation effect
- Programmers earn less than 30K a month before they are 30 years old. Where to go?
- 14 Best JavaScript Data Visualization Libraries
- Eight top-level VS Code extension plug-ins for the front end
- Complete guide to Node.js multithreading
- Four Solutions and Implementation of Converting HTML to PDF