如何克隆 JavaScript 类实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16024940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 02:58:21  来源:igfitidea点击:

How do I clone a JavaScript class instance?

javascriptjquery

提问by Petah

How do I clone a JavaScript class instance?

如何克隆 JavaScript 类实例?

I tried the normal jQuery extend, but that just returns a vanilla object. I have looked through many other answers on stack, but could not find how to clone an instance.

我尝试了普通的 jQuery 扩展,但这只是返回一个普通对象。我在堆栈上查看了许多其他答案,但找不到如何克隆实例。

function Parent(name) {
    this.name = name;
}

Parent.prototype.sayHello = function() {
    console.log('Hello my name is ' + this.name);
}

function Child(name) {
    Parent.call(this, name);
}

Child.prototype = Object.create(Parent.prototype);

var child = new Child('Billy');

var clone = $.extend(true, {}, child);
clone.name = 'Bob';

child.sayHello();
clone.sayHello();

console.log(child instanceof Child);
console.log(clone instanceof Child);

http://jsfiddle.net/39gjA/

http://jsfiddle.net/39gjA/

I would prefer that the clone was deep/recursive. I.E. all properties that are objects are cloned as well.

我更喜欢克隆是深的/递归的。IE 所有作为对象的属性也被克隆。

采纳答案by Bergi

How do I clone a JavaScript class instance?

如何克隆 JavaScript 类实例?

It's hardly possible if the instance was created with heavy use of closures in the constructor function. We may never now which internal values were set, and how to reproduce such a setup. Therefore, the easiest way would be if every class offered a clonefunction which knows what to do.

如果实例是在构造函数中大量使用闭包创建的,那几乎是不可能的。我们可能永远不会知道设置了哪些内部值,以及如何重现这样的设置。因此,最简单的方法是如果每个类都提供一个clone知道该做什么的函数。

normal jQuery extend just returns a vanilla object

普通的 jQuery 扩展只返回一个 vanilla 对象

Not necessarily, it returns what you passed in. Use

不一定,它会返回您传入的内容。使用

var clone = $.extend(true, Object.create(Object.getPrototypeOf(child)), child);

instead and your instanceofusage will work fine. Note that the truesignifies a "deep" copy which may or may not be what you want. Also, $.extendwill happily copy enumerable inherited properties as well, so you might need to use a more sophisticated extendfunction.

相反,您的instanceof使用将正常工作。请注意,true表示“深层”副本,这可能是您想要的,也可能不是。此外,$.extend也很乐意复制可枚举的继承属性,因此您可能需要使用更复杂的extend函数。

Or without jQuery at all, and only copying own, enumerable properties and only using a shallow copy:

或者根本没有 jQuery,只复制自己的可枚举属性并且只使用浅拷贝:

var clone = Object.assign(Object.create(Object.getPrototypeOf(child)), child);

But again, not all objects will be clonable in this way, see my first point above.

但同样,并非所有对象都可以通过这种方式克隆,请参阅我上面的第一点。

回答by Jesse

This will create a copy of an object with the same prototype (class) and same own properties (including enumerability/writability/getters/setters etc):

这将创建一个具有相同原型(类)和相同属性(包括可枚举性/可写性/getter/setter 等)的对象副本:

function clone(obj) {
  return Object.create(
    Object.getPrototypeOf(obj), 
    Object.getOwnPropertyDescriptors(obj) 
  );
}

(see here)

(见这里

It doesn't necessarily work well for builtin objects. For example Array.isArray(clone([]))is false, and clone(function () {})()says it is not a function, but for user created objects (either class instances or object literals) it works fine.

它不一定适用于内置对象。例如Array.isArray(clone([]))is false,并clone(function () {})()表示它不是函数,但对于用户创建的对象(类实例或对象文字),它工作正常。

To do a deep clone, you will have to loop over the property descriptors and clone the values recursively:

要进行深度克隆,您必须遍历属性描述符并递归地克隆值:

function deepClone(obj) {
  if (obj === null || typeof obj !== "object")
    return obj
  var props = Object.getOwnPropertyDescriptors(obj)
  for (var prop in props) {
    props[prop].value = deepClone(props[prop].value)
  }
  return Object.create(
    Object.getPrototypeOf(obj), 
    props
  )
}

回答by Daniel Aranda

I think that not necessarily needs to work with classes(or functions instances), you could extend prototype to apply OOP. My suggestion is that you extend prototype instead of create classes. jQuery is for DOM but not for advance object treatment so while $.extend could be helpful in some cases for complex stuff there are more advanced libraries.

我认为不一定需要使用类(或函数实例),您可以扩展原型以应用 OOP。我的建议是你扩展原型而不是创建类。jQuery 适用于 DOM,但不适用于高级对象处理,因此虽然 $.extend 在某些情况下可能对复杂的东西有帮助,但还有更高级的库。

You could use libraries like CloneJS to easily work with extendable objects: https://npmjs.org/package/clonejs

您可以使用像 CloneJS 这样的库来轻松处理可扩展对象:https://npmjs.org/package/clonejs

Just include this script: http://quadroid.github.io/clonejs/cdn/clone.min.js

只需包含此脚本:http: //quadroid.github.io/clonejs/cdn/clone.min.js

And try their own example:

并尝试他们自己的例子:

/// Forget about classes.    
//  Instead of creating class (function), create prototype (object):
var $duck = $object.clone({
    name: 'Unnamed',
    quack: function(){
        console.log( this.name +' Duck: Quack-quack!');
    }
});
$duck.quack();//Unnamed Duck: Quack-quack!

/// Inheritance is simple: 
var $talkingDuck = $duck.clone({
    quack: function(){
        this.applySuper('quack');
        console.log('My name is '+ this.name +'!');
    }       
});

/// Forget about the `new` operator, use .create() method instead:
var donald = $talkingDuck.create({name: 'Donald'});
donald.quack();// Donald Duck: Quack-quack! My name is Donald!

/// Forget about the `instanceof` operator, use JS native 
//  .isPrototypeOf() method instead:
$duck.isPrototypeOf(donald);// true

Also I think that Backbone.js applies the extension of prototype instead of creation of classes. They use _.extend

另外我认为 Backbone.js 应用原型的扩展而不是创建类。他们使用 _.extend

Some more references: http://www.2ality.com/2011/11/javascript-classes.htmlhttp://underscorejs.org/#extend

更多参考资料: http: //www.2ality.com/2011/11/javascript-classes.html http://underscorejs.org/#extend

回答by Daniel Aranda

You should try something like this:

你应该尝试这样的事情:

function clone_object(o){
    var n=Object.create(
        Object.getPrototypeOf(o),
        Object.getOwnPropertyNames(o).reduce(
            function(prev,cur){
                prev[cur]=Object.getOwnPropertyDescriptor(o,cur);
                return prev;
            },
            {}
        )
    );
    if(!Object.isExtensible(o)){Object.preventExtensions(n);}
    if(Object.isSealed(o)){Object.seal(n);}
    if(Object.isFrozen(o)){Object.freeze(n);}

    return n;
}

Narrative:

叙述:

  1. Create the new object using Object.createfrom a prototype and a properties object.
  2. For the prototype of the object to be cloned, use the prototype of the original object, using Object.getPrototypeOf.
  3. To create the properties object, loop over the own properties of the original object (using getOwnPropertyNames), and retrieve the property descriptor for each using getOwnPropertyDescriptor.
  4. Apply the extensibility/sealed/frozen characteristics of the original object to the clone.
  1. 使用Object.create原型和属性对象创建新对象。
  2. 对于要克隆的对象的原型,使用原始对象的原型,使用Object.getPrototypeOf.
  3. 要创建 properties 对象,请遍历原始对象的自己的属性( using getOwnPropertyNames),并为每个 using 检索属性描述符getOwnPropertyDescriptor
  4. 将原始对象的可扩展性/密封/冻结特性应用于克隆。

This will not deep-clone properties whose values are themselves objects. That's left as an exercise to the reader...YMMV.

这不会深度克隆值本身就是对象的属性。这留给读者作为练习...... YMMV。

回答by StudentsTea

I'd be interested to see someone benchmark this method against other approaches to cloning class instances in JavaScript that use native JavaScript.

我很想看到有人将此方法与在使用原生 JavaScript 的 JavaScript 中克隆类实例的其他方法进行对比。

// Defining a class
function MyClass(args) {

  this.foo = "bar";
}
// Avoid duplicate instances of class methods in RAM by adding them to the class prototype like this
MyClass.prototype.method = method;
MyClass.prototype.clone = clone;

// Define what your methods do
function method() {

  console.log(this.foo);
}

function clone() {

  var classScope = this;

  // Get the prototype of your class. This will create an object that has one key for every method in your class. I'm not sure if this will go up the prototype chain if you have subclasses. Someone ought to edit this answer to clarify that.
  var miniMe = Object.getPrototypeOf(this);

  // Iterate the properties of your class--all the internal key-value pairs that do get duplicated in RAM each time you instantiate the class.
  Object.keys(this).forEach(iterate);

  function iterate(key, index, list) {

      // Add each property to your clone
      miniMe[key] = classScope[key];
    }
    // Return the clone
  return miniMe;
}


// Instantiate your class
var me = new MyClass();
// Clone it
var miniMe = me.clone();

// Run some tests
Object.keys(Object.getPrototypeOf(me)).forEach(iterate);
Object.keys(me).forEach(iterate);

function iterate(property, index, list) {

  if (!miniMe.hasOwnProperty(property))
    throw new Error("miniMe does not have property " + property);
}

// Change the value of miniMe.foo and make sure it didn't impact the value of me.foo
miniMe.foo = "baz";
if (me.foo === miniMe.foo)
  throw new Error("me.foo should not equal miniMe.foo, because we changed its value");

回答by nmoliveira

Edited: When you know what kind of object you want to clone you can follow my example:

编辑:当您知道要克隆哪种对象时,可以按照我的示例进行操作:

In your example you can simply do something like this:

在您的示例中,您可以简单地执行以下操作:

var child = new Child('Billy');
var clone = new Child();
for (var prop in child) { 
  clone[prop] = child[prop];
}

I have updated your jsFiddle

我已经更新了你的jsFiddle