我做过类似obj = JSON.parse(JSON.stringify(o));但质疑效率的事情。
obj = JSON.parse(JSON.stringify(o));
我还看到了具有各种缺陷的递归复制函数。 我很惊讶不存在规范的解决方案。
现在有一个名为structured cloning的 JS 标准,它在 Node 11 及更高版本中进行实验性工作,将登陆浏览器,并且具有适用于现有系统的 polyfill。
structuredClone(value)
如果需要,首先加载 polyfill:
import structuredClone from '@ungap/structured-clone';
如果不使用DateS,功能,undefined,Infinity,正则表达式,地图,集合,斑点,的文件列表,ImageDatas,稀疏数组,类型化数组或其他复杂类型的对象中,一个很简单的一个衬垫深克隆的对象是:
Date
undefined
Infinity
JSON.parse(JSON.stringify(object))
const a = { string: 'string', number: 123, bool: false, nul: null, date: new Date(), // stringified undef: undefined, // lost inf: Infinity, // forced to 'null' re: /.*/, // lost } console.log(a); console.log(typeof a.date); // Date object const clone = JSON.parse(JSON.stringify(a)); console.log(clone); console.log(typeof clone.date); // result of .toISOString()
由于克隆对象并非易事(复杂类型、循环引用、函数等),大多数主要库都提供了克隆对象的函数。不要重新发明轮子- 如果您已经在使用库,请检查它是否具有对象克隆功能。例如,
cloneDeep
angular.copy
jQuery.extend(true, { }, oldObject)
.clone()
just-clone
为了完整起见,请注意 ES6 提供了两种浅拷贝机制:Object.assign() 和 spread syntax. 。它将所有可枚举属性的值从一个对象复制到另一个对象。例如:
Object.assign()
var A1 = {a: "2"}; var A2 = Object.assign({}, A1); var A3 = {...A1}; // Spread Syntax
2021更新:在structuredClone全局函数即将登陆浏览器,Node.js的,和杰诺。
structuredClone
HTML 标准包括一个内部结构化克隆/序列化算法,可以创建对象的深层克隆。它仍然仅限于某些内置类型,但除了 JSON 支持的少数类型之外,它还支持日期、正则表达式、地图、集合、Blob、文件列表、图像数据、稀疏数组、类型化数组,以及将来可能更多. 它还保留克隆数据中的引用,使其支持可能导致 JSON 错误的循环和递归结构。
在structuredClone全球功能将很快被Node.js的提供:
const clone = structuredClone(original);
在那之前:v8Node.js 中的模块当前(从 Node 11 开始)直接公开结构化序列化 API,但此功能仍标记为“实验性”,并且在未来版本中可能会更改或删除。如果您使用的是兼容版本,则克隆对象非常简单:
v8
const v8 = require('v8'); const structuredClone = obj => { return v8.deserialize(v8.serialize(obj)); };
在structuredClone全局函数很快就会被所有主流浏览器提供(前面已经讨论WHATWG / HTML#793 GitHub上)。它看起来/看起来像这样:
在发布之前,浏览器的结构化克隆实现只是间接公开。
使用现有 API 创建结构化克隆的开销较低的方法是通过MessageChannels 的一个端口发布数据。另一个端口将发出一个message带有附加.data. 不幸的是,监听这些事件必然是异步的,而同步的替代方案不太实用。
message
.data
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); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; main();
同步创建结构化克隆没有好的选择。这里有一些不切实际的技巧。
history.pushState()并且history.replaceState()都创建了第一个参数的结构化克隆,并将该值分配给history.state. 您可以使用它来创建任何对象的结构化克隆,如下所示:
history.pushState()
history.replaceState()
history.state
const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; };
Hide code snippet
'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; main();
虽然是同步的,但这可能会非常慢。它会产生与操纵浏览器历史记录相关的所有开销。重复调用此方法可能会导致 Chrome 暂时无响应。
该Notification构造函数创建其相关数据的结构化克隆。它还尝试向用户显示浏览器通知,但除非您请求通知权限,否则这将静默失败。如果您有其他用途的许可,我们将立即关闭我们创建的通知。
Notification
const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; };
'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const n = new Notification('', {data: obj, silent: true}); n.close(); return n.data; }; main();