我正在尝试在JavaScript中克隆一个对象。我创建了自己的具有原型函数的"类"。
我的问题:当我克隆对象时,克隆无法访问/调用任何原型函数。
当我去访问克隆的原型功能时出现错误:
clone.render 不是一个函数
你能告诉我如何克隆一个对象并保持其原型功能
这个简单的JSFiddle演示了我得到的错误:http://jsfiddle.net/VHEFb/1/
function cloneObject(obj)
{
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; ++i) {
copy[i] = cloneObject(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = cloneObject(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
function MyObject(name)
{
this.name = name;
// I have arrays stored in this object also so a simple cloneNode(true) call wont copy those
// thus the need for the function cloneObject();
}
MyObject.prototype.render = function()
{
alert("Render executing: "+this.name);
}
var base = new MyObject("base");
var clone = cloneObject(base);
clone.name = "clone";
base.render();
clone.render(); // Error here: "clone.render is not a function"
对代码的一些评论:
> if (obj instanceof Date) {
> var copy = new Date();
> copy.setTime(obj.getTime());
可以是:
if (obj instanceof Date) {
var copy = new Date(obj);
和
> if (obj instanceof Array) {
如果 obj 是来自另一个全局上下文(如 iFrame)的数组,则返回 false。考虑:
if (o && !(o.constructor.toString().indexOf("Array") == -1))
> var copy = [];
> for (var i = 0, len = obj.length; i < len; ++i) {
> copy[i] = cloneObject(obj[i]);
> }
使用 slice
可以更高效、更准确地将一个数组的索引复制到另一个数组:
var copy = obj.slice();
尽管您会错过可能已添加的任何其他非数字属性。循环超过 0 到长度将向克隆添加稀疏数组中不存在的属性(例如,省略号将成为未定义的成员)。
至于克隆部分...
在复制对象属性的部分中,会将所有属性(包括原始[[Prototype]]
链上的属性)直接复制到"克隆"对象。真正"克隆"对象的唯一方法是将其[[Prototype]]
设置为与原始对象相同的对象,然后将原始对象上的可枚举属性(使用 hasOwnProperty
过滤)复制到克隆中。
第二部分是微不足道的,第一部分不是(在一般意义上),因为你不能保证对象的构造函数属性引用其prototype
是其[[Prototype]]
的对象,也不能保证构造函数的原型在此期间没有改变(即是一个不同的对象)。
你可以得到的最接近的是使用Lasse Reichstein Nielsen的clone
(由Douglas Crockford推广为beget
),它使原始对象成为克隆的[[Prototype]]
,然后将构造函数设置为同一对象。尽管您可能仍然需要复制可枚举的自己的属性,以便它们掩盖原始属性的同名属性。
因此,您实际上只能在受限上下文中克隆对象,通常无法做到这一点。通常,这种实现会导致一种设计,即您不需要一般地克隆对象。
而不是
var copy = {};
用
var copy = new obj.constructor;
您的函数可以简化为:
function cloneObject(obj)
{
obj = obj && obj instanceof Object ? obj : '';
// Handle Date (return new Date object with old value)
if (obj instanceof Date) {
return new Date(obj);
}
// Handle Array (return a full slice of the array)
if (obj instanceof Array) {
return obj.slice();
}
// Handle Object
if (obj instanceof Object) {
var copy = new obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)){
if (obj[attr] instanceof Object){
copy[attr] = cloneObject(obj[attr]);
} else {
copy[attr] = obj[attr];
}
}
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
这是一个工作 jsfiddle
我会使用要克隆的对象的构造函数来实例化克隆对象:
var copy = {};
将是
var copy = new obj.constructor();
这是一个快速的响应,我没有考虑过这种解决方案的缺点(我正在考虑重型构造函数),但是,乍一看它应该有效(我不会提到(或诉诸)深奥的方法作为__proto__
)。
更新:
您应该求助于object.create
来解决此问题。
var copy = {};
将是
var copy = Object.create(obj.constructor.prototype);
通过这种方式,不会调用原始构造函数来创建对象(想想执行冗长的 ajax 调用以从服务器检索数据的构造函数),因为 Object.create 等效于
Object.create = function (proto) {
function F() {}
F.prototype = proto;
return new F();
};
如果您使用的 javascript 引擎不支持此功能,您可以使用此代码(它已添加到 ecmascript 5 规范中)