如何克隆JavaScript类实例?

如何克隆JavaScript类实例?

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

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/

我希望克隆是深度/递归的。 IE也克隆了作为对象的所有属性。

如何克隆JavaScript类实例?

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

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

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

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

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

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

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

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

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

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

(见这里 )

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

要进行深度克隆,您必须循环遍历属性描述符并以递归方式克隆值:

 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 ) } 

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

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

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

并尝试自己的例子:

 /// 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 

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

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

你应该尝试这样的事情:

 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; } 

叙述:

  1. 使用原型和属性对象中的Object.create创建新对象。
  2. 对于要克隆的对象的原型,使用Object.getPrototypeOf使用原始对象的原型。
  3. 要创建属性对象,请循环遍历原始对象的自身属性(使用getOwnPropertyNames ),并使用getOwnPropertyNames检索每个属性的描述符。
  4. 将原始对象的可扩展性/密封/冻结特征应用于克隆。

这不会深度克隆其值本身就是对象的属性。 这留给了读者一个练习… YMMV。

我有兴趣看到有人将此方法与使用本机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"); 

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

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

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

我已经更新了你的jsFiddle