我最近一直在研究的项目让我需要创建一个函数,它可以返回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 = { }
的对象)和具有适当原型/构造函数的对象?如果是这样,是否还有一种复制此类对象的简单通用方法?我对问题的第二部分的期望是否定的,并且我仍然需要特殊处理来调用构造函数,但我仍然想确定地知道。
P.S。如果有人对上下文感到好奇,那么该项目要求我在服务器上操作大量项目,但是以不同方式操作不同的连接客户端。我能想到的最简单的方法是创建一个主列表,然后让服务器克隆一个新的副本来操作而不需要为每个连接的新客户端更改主列表,因此需要copyObj()
。 / p>
编辑:我可能应该在原始问题中提到这一点 - 这是作为服务器而不是在浏览器中运行node.js,因此浏览器交叉兼容性不是问题。
编辑2:为了不会过多地混淆评论,我将在此处提及:我尝试针对{{1}快速对copyObj()
函数进行基准测试利用上面的示例对象进行利用。我的版本似乎在JSON方法的大约75%的时间内运行(100万份拷贝花了大约3.2秒,而JSON大约需要4.4秒)。所以这让我觉得花时间写自己的感觉更好。
编辑3:在Vitum.us的答案中处理对象类型列表,我将JSON.parse(JSON.stringify(obj))
函数的更新版本汇总在一起。我没有对它进行过广泛的测试,性能比旧版本差2倍,但我认为它应该适用于所有内置类型(假设列表已完成)。
copyObj()
我现在正在使用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;
}
属性,正如Mike建议的那样,它似乎正在做这个伎俩。到目前为止,我已使用.constructor
和RegExp
个对象对其进行了测试,它们似乎都正确复制了。你们有没有看到任何公然(或巧妙)不正确的事情?
答案 0 :(得分:0)
检测普通JS对象(使用{}
或new Object
创建)的一种方法是使用jQuery方法jQuery.isPlainObject。
但是,文档中说“ Host对象有许多不一致的地方,这些不一致性难以在跨平台上进行检测。因此,$。isPlainObject()可能会在某些情况下对浏览器进行不一致的评估。” em>“是否可以对node.js进行可靠的测试。
编辑以回应您的评论:您可以将jQuery与node.js一起使用,请参阅此问题:Can I use jQuery with Node.js?
否则,也可以将方法的jQuery实现复制到项目中,因为MIT许可证(https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt)似乎允许这样做。 jQuery实现:
isPlainObject: function( obj ) {
// Not plain objects:
// - Any object or value whose internal [[Class]] property is not "[object Object]"
// - DOM nodes
// - window
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
// Support: Firefox <20
// The try/catch suppresses exceptions thrown when attempting to access
// the "constructor" property of certain host objects, ie. |window.location|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
try {
if ( obj.constructor &&
!core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
return false;
}
} catch ( e ) {
return false;
}
// If the function hasn't returned already, we're confident that
// |obj| is a plain object, created by {} or constructed with new Object
return true;
}
答案 1 :(得分:0)
您可以使用它来检测对象是否是正则表达式
Object.prototype.toString.call( regexpObject ) == "[object RegExp]"
这是获取对象类的规范中提到的方法。
来自 ECMAScript 5,第8.6.2节“对象内部属性和方法”:
[[Class]]内部属性的值由此规范为每种内置对象定义。主机对象的[[Class]]内部属性的值可以是除“Arguments”,“Array”,“Boolean”,“Date”,“Error”,“Function”之一之外的任何String值, “JSON”,“Math”,“Number”,“Object”,“RegExp”和“String”。内部使用[[Class]]内部属性的值来区分不同类型的对象。请注意,除了通过Object.prototype.toString之外,此规范不提供程序访问该值的任何方法(参见15.2.4.2)。
RegExp是第15.10节RegExp(RegularExpression)对象中规范中定义的一类对象:
RegExp对象包含正则表达式和关联的标志。
然后,您可以使用new RegExp()
var oldObject = /[a-z]+/;
var newObject = new RegExp(oldObject);