Created
November 5, 2018 12:11
-
-
Save laphilosophia/ab3858ba3ee930f1651210f4b6ba609f to your computer and use it in GitHub Desktop.
Object copy methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SHALLOW COPY | |
const obj1 = { | |
a: 1, | |
b: 2, | |
c: 'three', | |
d: new Date() | |
} | |
const obj2 = { | |
e: '1', | |
f: '2', | |
g: 3, | |
h: new Function() | |
} | |
const copySpread = { ...obj1, | |
...obj2 | |
} | |
const copyAssign = Object.assign({}, obj1, obj2) | |
// DEEP COPY (WITH CAVEATS) | |
const obj3 = { | |
i: 0, | |
j: { | |
b: 1, | |
c: { | |
d: 0 | |
} | |
} | |
} | |
const copiedObject = JSON.parse(JSON.stringify(obj3)) | |
// DEEP COPY (WITH FEWER CAVEATS) | |
// Via MessageChannels: | |
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.") | |
} | |
// Via the history API: | |
const structuredClone = obj => { | |
const oldState = history.state | |
history.replaceState(obj, null) | |
const clonedObj = history.state | |
history.replaceState(oldState, null) | |
return clonedObj | |
} | |
// Via the notification API: | |
const structuredClone = obj => { | |
const n = new Notification('', { | |
data: obj, | |
silent: true | |
}) | |
n.onshow = n.close.bind(n) | |
return n.data | |
}; | |
// DEEP COPY IN NODE.JS | |
`` | |
` | |
Unfortunately again, the structured clone algorithm is currently | |
only available to browser based applications. | |
For the server side, one can use lodash's cloneDeep method, which | |
is also loosely based on the structured clone algorithm. | |
` | |
`` | |
// CONCLUSION | |
function deepClone(obj) { | |
let 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 (let i = 0, len = obj.length; i < len; i++) { | |
copy[i] = clone(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] = clone(obj[attr]) | |
} | |
return copy | |
} | |
throw new Error("Unable to copy obj as type isn't supported " + obj.constructor.name) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment