3. Crazy geek


The original:
https://smalldata.tech/blog/2…


This article first send WeChat messages public number: front-end pioneer welcome attention, every day to you push fresh front-end technology articles


Before I get started, let me cover some basics. A JavaScript object is simply a pointer to a location in memory. These Pointers are mutable, that is, they can be reassigned. So just copying this pointer will result 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 above example, objects Foo and Bar can both change as each other changes. So when copying objects in JavaScript, there are a few considerations to consider.

Shallow copy

You can use the extended syntax or Object.assign(…) if the Object you are manipulating has properties of value types.

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 properties from multiple different source objects into a single 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 problem with this approach is that if the object’s properties are also objects, then all that is actually copied is the pointer, which is the same as doing var bar = foo; The effect is the same as in the first section of 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 first 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 and no circular references. One common thing that can’t be serialized is the date object — although it displays a strung ISO date format, JSON.parse only parses it as a string, not a date type.

Deep copy (less restrictive)

For more complex scenarios, we can use a new algorithm provided by HTML5 called Structured Cloning. However, as of this publication, some of the built-in types are still not supported, but there are many more that JSON.parse supports: 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, which are not supported in the serialization mentioned earlier.

There is currently no way to call structured cloning directly, but some of the new browser features underlie this algorithm. So deep copy objects may need to depend on a number of environments to implement.

Via Messagechannels: The principle is borrowed from the serialization algorithm used in communication. Since 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();

The Via the History API: history.pushState() and history.replaceState() both make a structured clone of their first argument! It is important to note that this method is synchronous, because operating on the browser history is not very fast and will cause the browser to freeze if called frequently.

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 associated data. Note that it will try to show the browser notifications to the user, but will do nothing unless it receives a request from the user for permission to show the notifications. Once the user clicks “yes”, the notification is immediately turned off.

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

The 8.0.0 version of Node.js provides a serialization API that is comparable to structured cloning. At the time of publication, however, the 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();

For a more stable approach below 8.0.0, consider Lodash’s cloneDeep function, which is also more or less based on the idea of the structured cloning algorithm.

conclusion

The best algorithm for copying objects in JavaScript depends a lot on the context in which they are used and the type of objects you need to copy. Although lodash is the safest generic deep copy function, you may be able to achieve a more efficient implementation if you wrap it yourself. Here is a simple deep copy that works for the Date object as well:

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 is freely available and object copying is no longer a headache


This article first send WeChat messages public number: front-end pioneer

Welcome to scan the two-dimensional code to pay attention to the public number, every day to push you fresh front-end technology articles


Read on for the other great articles in this column:

  • 12 Amazing CSS Experiment Projects
  • 50 React Interview Questions You Must Know
  • What are the front-end interview questions at the world’s top companies
  • 11 of the best JavaScript dynamic effects libraries
  • CSS Flexbox Visualization Manual
  • React from a designer’s point of view
  • The holidays are boring? Write a little brain game in JavaScript!
  • How does CSS sticky positioning work
  • A step-by-step guide to implementing animations using HTML5 SVG
  • Programmer 30 years old before the monthly salary is less than 30K, which way to go
  • 14 of the best JavaScript data visualization libraries
  • 8 top VS Code extensions for the front end
  • A complete guide to Node.js multithreading
  • Convert HTML to PDF 4 solutions and implementation