如何克隆javascript ES6类实例

Tom*_*Tom 70 javascript node.js ecmascript-6 es6-class

如何使用ES6克隆Javascript类实例.

我对基于jquery或$ extend的解决方案不感兴趣.

我已经看到对对象克隆的相当古老的讨论表明这个问题非常复杂,但是使用ES6可以得到一个非常简单的解决方案 - 我会把它放在下面,看看人们是否认为它是令人满意的.

编辑:建议我的问题是重复的; 我看到了这个答案,但它已经有7年的历史,并且使用前ES6 js涉及非常复杂的答案.我建议我的问题,允许ES6,有一个非常简单的解决方案.

flo*_*ori 89

let clone = Object.assign( Object.create( Object.getPrototypeOf(orig)), orig)
Run Code Online (Sandbox Code Playgroud)

我尝试了很多,最后这对我有用.

它还避免设置原型,因为他们说它会大大减慢代码的速度.

这是一个单行!

  • @ Mr.Lavalamp你怎么能复制(也)静态方法? (5认同)
  • 这不会复制静态方法,因为它们实际上不是可枚举的自己的属性。 (3认同)
  • 静态方法不需要克隆!它们是类的一部分而不是实例的一部分 (3认同)
  • 不要指望它克隆本身就是对象的属性:https://jsbin.com/qeziwetexu/edit?js,console (2认同)

HSL*_*SLM 25

我喜欢几乎所有的答案。我遇到了这个问题,为了解决它,我将通过定义一个方法来手动完成它clone(),并在其中从头开始构建整个对象。对我来说,这是有道理的,因为结果对象自然与克隆对象具有相同的类型。

打字稿示例:

export default class ClassName {
    private name: string;
    private anotherVariable: string;
   
    constructor(name: string, anotherVariable: string) {
        this.name = name;
        this.anotherVariable = anotherVariable;
    }

    public clone(): ClassName {
        return new ClassName(this.name, this.anotherVariable);
    }
}
Run Code Online (Sandbox Code Playgroud)

我喜欢这个解决方案,因为它看起来更“面向对象”

  • 这确实是前进的方向。获得通用的克隆机制是“非常”困难的。不可能让它适用于每一个案例。总是会有奇怪和不一致的类。因此,确保对象本身可克隆是唯一的方法。作为替代(或补充),可以有一个从实例进行克隆的方法,例如“public static clone(instance: MyClass): MyClass)”,它具有相同的处理克隆的想法,只是专门制作它实例外部。 (7认同)
  • 这是一个很好的答案,上面的评论是一个很好的建议。还值得指出的是,对象和数组属性是通过引用传递的,因此您也需要克隆它们,否则可能会遭受意想不到的副作用!以下是说明要点:https://gist.github.com/sscovil/def81066dc59e6ff5084a499d9855253 (5认同)

Tom*_*Tom 9

const clone = Object.assign( {}, instanceOfBlah );
Object.setPrototypeOf( clone, Blah.prototype );
Run Code Online (Sandbox Code Playgroud)

注意Object.assign的特征:它执行浅表复制,并且不复制类方法。

如果您想要深层副本或对副本有更多控制,则可以使用lodash克隆功能

  • @barbatus 不过,它使用了错误的原型,`Blah.prototype != instanceOfBlah`。你应该使用`Object.getPrototypeOf(instanceOfBlah)` (3认同)
  • 由于“ Object.create”会使用指定的原型创建新对象,所以为什么不直接使用“ const clone = Object.assign(Object.create(instanceOfBlah),instanceOfBlah)”。同样,类方法也将被复制。 (2认同)

Cas*_*rin 5

太长了;

// Use this approach
//Method 1 - clone will inherit the prototype methods of the original.
    let cloneWithPrototype = Object.assign(Object.create(Object.getPrototypeOf(original)), original); 
Run Code Online (Sandbox Code Playgroud)

在 Javascript 中,不建议对原型进行扩展,这会在您对代码/组件进行测试时导致问题。单元测试框架不会自动假设您的原型扩展。所以这不是一个好的做法。这里有更多关于原型扩展的解释为什么扩展本机对象是一种不好的做法?

要在 JavaScript 中克隆对象,没有一种简单或直接的方法。这是使用“浅复制”的第一个实例:

1 -> 浅克隆:

class Employee {
    constructor(first, last, street) {
        this.firstName = first;
        this.lastName = last;
        this.address = { street: street };
    }

    logFullName() {
        console.log(this.firstName + ' ' + this.lastName);
    }
}

let original = new Employee('Cassio', 'Seffrin', 'Street A, 23');

//Method 1 - clone will inherit the prototype methods of the original.
let cloneWithPrototype = Object.assign(Object.create(Object.getPrototypeOf(original)), original); 

//Method 2 - object.assing() will not clone the Prototype.
let cloneWithoutPrototype =  Object.assign({},original); 

//Method 3 - the same of object assign but shorter syntax using "spread operator"
let clone3 = { ...original }; 

//tests
cloneWithoutPrototype.firstName = 'John';
cloneWithoutPrototype.address.street = 'Street B, 99'; //will not be cloned
Run Code Online (Sandbox Code Playgroud)

结果:

原始的.logFullName();

结果:卡西奥·塞夫林

克隆WithPrototype.logFullName();

结果:卡西奥·塞夫林

原始地址.街道;

result: 'Street B, 99' // 注意原始子对象已更改

注意:如果实例将闭包作为自己的属性,则此方法将不会包装它。(阅读有关闭包的更多信息)另外,子对象“地址”不会被克隆。

克隆WithoutPrototype.logFullName()

不管用。克隆不会继承原始版本的任何原型方法。

cloneWithPrototype.logFullName()

会起作用,因为克隆也会复制它的原型。

要使用 Object.assign 克隆数组:

let cloneArr = array.map((a) => Object.assign({}, a));
Run Code Online (Sandbox Code Playgroud)

使用 ECMAScript 扩展语法克隆数组:

let cloneArrSpread = array.map((a) => ({ ...a }));
Run Code Online (Sandbox Code Playgroud)

2 -> 深度克隆:

要归档一个全新的对象引用,我们可以使用 JSON.stringify() 将原始对象解析为字符串,然后将其解析回 JSON.parse()。

let deepClone = JSON.parse(JSON.stringify(original));
Run Code Online (Sandbox Code Playgroud)

通过深度克隆,将保留对地址的引用。然而,deepClone 原型将会丢失,因此 deepClone.logFullName() 将不起作用。

3 -> 第三方库:

另一种选择是使用第三方库,例如 loadash 或 underscore。他们将创建一个新对象,并将每个值从原始对象复制到新对象,并将其引用保留在内存中。

下划线:让cloneUnderscore = _(original).clone();

Loadash克隆:var cloneLodash = _.cloneDeep(original);

lodash 或 underscore 的缺点是需要在项目中包含一些额外的库。然而,它们是不错的选择,并且也能产生高性能结果。

  • 当分配给“{}”时,克隆将不会继承原始的任何原型方法。`clone.logFullName()` 根本不起作用。你之前的`Object.assign( Object.create(Object.getPrototypeOf(eOriginal)), eOriginal)` 很好,你为什么要改变它? (3认同)
  • 我感谢您的帮助@Bergi,请现在发表您的意见。我已经完成了该版本。我想现在答案已经涵盖了几乎所有问题。谢谢! (2认同)
  • 有时我们只需要更简单的方法。如果您不需要原型和复杂对象,可能只需“clone = { ...original }”就可以解决问题 (2认同)