深度克隆类实例 JavaScript

Men*_*ndy 10 javascript ecmascript-6

我试图找出一种方法来深度克隆 JS 类实例,同时保留链中的所有原型。

我已经看到了如何深度克隆和对象:

JSON.parse(JSON.stringify(instance))
Run Code Online (Sandbox Code Playgroud)

我已经看到了如何制作类实例的浅拷贝:

Object.assign( Object.create( Object.getPrototypeOf(instance) ), instance)
Run Code Online (Sandbox Code Playgroud)

但我的问题是,有没有办法深度克隆一个类的实例?

jfr*_*d00 9

没有万无一失的方法来克隆 JS 中所有可能类型的对象,特别是如果它包含对其他对象的引用。通用克隆参数不知道克隆中的对象引用是否应该包含相同的引用(例如公共父对象),或者它是否需要克隆我也有引用的对象。一般不可能知道,因为它实际上取决于对象的实现。

如果存在对对象的循环引用,例如父对子和子对父,则它变得更加复杂。

再举一个例子,假设一个对象作为其构造函数的一部分,它创建一个唯一的对象 ID,向某个服务注册该 ID,然后将该 ID 存储在其实例数据中。通用克隆机制无法知道创建新对象所需的逻辑(生成新 ID 并将其注册到某些服务)。这种类型的逻辑必须由特定于该对象的代码来完成,该对象知道该做什么。

作为另一个例子,构造函数可能会创建无法从外部复制的闭包(可以访问私有信息)。

作为另一个例子,构造函数可能将方法绑定到它自己的实例,而泛型克隆不知道它需要做什么。

克隆对象的最佳方法是使用内置于对象实现中的代码,该代码知道如何克隆自身,例如.clone()在对象本身上添加方法(或随意命名),并让对象支持复制自身. 然后,它可以对任何实例数据做正确的事情,只有对象实现本身才能知道如何处理所有可能类型的实例数据。


Col*_*rus 7

我推荐使用 Lodash 的cloneDeep。它适用于所有类型,函数和符号是通过引用复制的。

JSON.parse(JSON.stringify(myObject))当存在循环引用时,使用该方式是有问题的。此外,它将对象的方法替换undefined为属性并重新排序。


Men*_*ndy 4

一种可能的方法是调用结构化克隆算法,通过 MessageChannel 发送实例来进行克隆,如下所示:

function deepClone(instance) {
    return new Promise(resolve => {
        const messageChannel = new MessageChannel();
        messageChannel.port2.onmessage = e => resolve(e.data);
        messageChannel.port1.postMessage(instance);
    });
}
Run Code Online (Sandbox Code Playgroud)

这将处理 Map、Set、Date、RegExp、Blob、ArrayBuffer等类型,甚至可以克隆自定义类。

可以在此处找到触发结构化克隆的一些其他方法。