1

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是你唯一可靠的跨浏览器的选择。


发条橙子
399 声望14 粉丝

我爱吃西瓜