JavaScript 深拷贝性能分析(汉化版)
JavaScript 深拷贝性能分析
Object.assign()
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。
JSON.parse
过去浏览器内部传递的对象是使用JSON进行序列化的。 现代浏览器序列化将使用结构化克隆算法。这将会使更多对象可以被安全的传递。
JSON parse的优缺点
优点
-
代码简单
const obj = /* ... */ const copy = JSON.parse(JSON.stringify(obj))
缺点
- 如果对象复杂,你可能创建一个临时的,很大的字符串,只是为了把它重新放回解析器。
-
无法解决处理循环对象, 如当您构建树状数据结构,其中一个节点引用其父级,而父级又引用其子级。
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!
- 诸如
Map
,Set
,RegExp
,Date
,ArrayBuffer
和其他内置类型在进行序列化时会丢失
结构化克隆算法
因为它支持包含循环图的对象的序列化 ---对象可以引用在同一个图中引用其他对象的对象。此外,在某些情况下,结构化克隆算法可能比JSON更高效。
结构化算法优于JSON
的地方
优于 JSON 的地方
- 结构化克隆可以复制 RegExp 对象。
- 结构化克隆可以复制 Blob、File 以及 FileList 对象。
- 结构化克隆可以复制 ImageData 对象。CanvasPixelArray
的克隆粒度将会跟原始对象相同,并且复制出来相同的像素数据。
- 结构化克隆可以正确的复制有循环引用的对象
结构化克隆所不能做到的
- Error 以及 Function 对象是不能被结构化克隆算法复制的;如果你尝试这样子去做,这会导致抛出 DATA_CLONE_ERR 的异常。
- 企图去克隆 DOM 节点同样会抛出 DATA_CLONE_ERROR 异常。
-
对象的某些特定参数也不会被保留
- RegExp 对象的 lastIndex 字段不会被保留
- 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write,因为这是默认的情况下。
- 原形链上的属性也不会被追踪以及复制。
- symbols 不会被复制
MessageChannel
Channel Messaging API的Channel Messaging接口允许我们创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据。
消息通道的传递是异步的,使用结构化克隆算法。
Note: 此特性在 Web Worker 中可用。
兼容性如下:
History API
HTML5引入了 history.pushState()
和 history.replaceState()
方法,它们分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate
配合使用。
- pushState
history.pushState(data({a: 'hi'}), name("tdy"), url("bar.html"))
改变浏览器的url显示,但是浏览器本身不会去做任何的改变。 - history.replaceState() 参数和
pushState
一致,修改当前页面的信息 - window.onpopstate
调用history.pushState()或者history.replaceState()不会触发popstate事件. popstate事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮(或者在JavaScript中调用history.back()、history.forward()、history.go()方法).信息保存在state
属性上
pushState或replaceState时需要复制一份状态对象,这个状态对象使用结构化克隆而且是同步的。
最大储存对象大小为640KB。
Safari 浏览器对replaceState调用的限制数量为 30 秒内 100 次。
兼容性如下:
Notification API
简洁明了
function structuralClone(obj) {
return new Notification('', {data: obj, silent: true}).data
}
const obj = /* ... */
const clone = structuralClone(obj)
兼容性欠佳,并且,Safari 总是返回undefined。
最后
如果您没有循环对象,并且不需要保留内置类型,则可以使用跨浏览器的JSON.parse(JSON.stringify())获得最快的克隆性能。
如果你想要一个适当的结构化克隆,MessageChannel是你唯一可靠的跨浏览器的选择。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。