深度克隆类实例JavaScript

时间:2019-08-18 06:02:18

标签: javascript ecmascript-6

我试图找到一种在保留所有原型原型的同时深度克隆JS类实例的方法。

我已经了解了如何深入克隆和对象:

JSON.parse(JSON.stringify(instance))

我已经看到了如何制作类实例的浅表副本:

Object.assign( Object.create( Object.getPrototypeOf(instance) ), instance)

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

3 个答案:

答案 0 :(得分:7)

我建议使用Lodash的cloneDeep。它适用于所有类型,功能和符号均通过引用复制。

在存在循环引用时,使用JSON.parse(JSON.stringify(myObject))方式会出现问题。同样,它将对象的方法替换为undefined并重新排列属性。

答案 1 :(得分:3)

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

如果有对对象的循环引用,例如父级对子级和子级对父级,则会变得更加复杂。

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

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

再举一个例子,构造函数可能会将方法绑定到自己的实例,而泛型克隆将不需要这样做。

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

答案 2 :(得分:1)

可能的破解方法是通过MessageChannel发送实例来调用structured clone algorithm进行克隆,如下所示:

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

这将处理诸如Map,Set,Date,RegExp,Blob,ArrayBuffer和more之类的类型,甚至可以克隆自定义类。

可以找到其他一些触发结构化克隆的方法here

注意:这可能很慢。