我最近一直在做的一个项目让我需要创建一个函数,该函数可以返回 JSON 对象的完整副本,递归地复制任何内部对象。经过几次失败的尝试,我想出了这个:
function copyObj(obj) {
var copy;
if (obj instanceof Array) {
copy = [];
for (var i in obj) {
copy.push(copyObj(obj[i]));
}
}
else if (obj instanceof Object) {
copy = {};
for (var prop in obj) {
copy[prop] = copyObj(obj[prop]);
}
}
else {
copy = obj;
}
return copy;
}
该函数非常适合我的目的,即复制仅包含原始类型、数组和嵌套的通用 JSON 对象的对象。例如,它将返回一个完美的副本:{ prop1:0, prop2:'test', prop3:[1, 2, 3], prop4:{ subprop1:['a', 'b', 'c'], subprop2:false } }
.
不过,这个函数有一点让我烦恼——它无法处理任何其他类型的对象(例如RegExp
对象)。我想通过添加处理它们的能力来改进它,但同时我真的宁愿不只是拥有一堵巨大的else if (obj instanceof [insert object type here])
. 因此,我的问题是:在 JavaScript 中是否有一种简单的方法来区分通用对象(即声明为的对象var obj = { }
)和具有适当原型/构造函数的对象?如果是这样,是否还有一种复制此类对象的简单通用方法?我对问题的第二部分的期望是否定的,我仍然需要特殊处理来调用构造函数,但我仍然想确定地知道这两种方式。
PS 如果有人对上下文感到好奇,该项目需要我操作服务器上的大量项目,但对于不同的连接客户端以不同的方式。我能想到的最简单的处理方法是创建一个主列表,然后让服务器克隆一个新副本以进行操作,而无需更改每个连接的新客户端的主列表,因此需要copyObj()
.
编辑:我可能应该在原始问题中提到这一点 - 这是使用 node.js 作为服务器而不是在浏览器中运行的,因此浏览器交叉兼容性不是问题。
编辑 2:为了不让评论过于混乱,我会在这里提及:我尝试使用上面的示例对象对我的copyObj()
函数进行快速基准测试。JSON.parse(JSON.stringify(obj))
我的版本的运行时间似乎是 JSON 方法所用时间的大约 75%(我的 100 万份副本耗时约 3.2 秒,JSON 耗时约 4.4 秒)。所以这让我觉得花时间写自己的感觉好多了。
编辑 3:根据 Vitum.us 的答案中的对象类型列表,我将我的copyObj()
函数的更新版本放在一起。我没有对它进行广泛的测试,性能比旧版本差大约 2 倍,但我认为它实际上应该适用于所有内置类型(假设列表是完整的)。
function copyObjNew(obj) {
var copy;
if (obj.constructor === Object) {
// Generic objects
copy = {};
for (var prop in obj) {
copy[prop] = copyObjNew(obj[prop]);
}
}
else if (obj.constructor === Array) {
// Arrays
copy = [];
for (var i in obj) {
copy.push(copyObjNew(obj[i]));
}
}
else if (obj.constructor === Number || obj.constructor === String || obj.constructor === Boolean) {
// Primitives
copy = obj;
}
else {
// Any other type of object
copy = new obj.constructor(obj);
}
return copy;
}
正如迈克建议的那样,我现在正在使用该.constructor
物业,而且它似乎正在发挥作用。到目前为止,我已经使用RegExp
和Date
对象对其进行了测试,它们似乎都可以正确复制。你们有没有看到任何明显(或微妙)不正确的地方?