区分JavaScript中定义的和通用的JSON对象

我最近一直在处理的一个项目给了我创build一个函数的需求,它可以返回一个JSON对象的完整副本,recursion地复制任何内部对象。 经过几次失败的尝试,我想出了这个:

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

该函数对于我的目的完美工作,即复制只包含原始types,数组和嵌套的genericsJSON对象的对象。 例如,它会返回一个完美的副本: { prop1:0, prop2:'test', prop3:[1, 2, 3], prop4:{ subprop1:['a', 'b', 'c'], subprop2:false } }

关于这个函数,有一件事情让我唠叨 – 它无法处理任何其他types的对象(例如RegExp对象)。 我想通过增加处理它们的能力来改善它,但是同时我真的不希望有一大堆else if (obj instanceof [insert object type here]) 。 因此,我的问题是:在JavaScript中有一种简单的方法来区分通用对象(即声明为var obj = { } )和具有适当原型/构造函数的对象吗? 如果是这样,是否还有一个简单的复制这种对象的普遍方式? 我对这个问题的第二部分的期望是否定的,我仍然需要特殊的处理来调用构造函数,但是我仍然想要肯定地知道任何一种方式。

PS如果有人对上下文感到好奇,该项目要求我在服务器上处理大量项目,但对于不同的连接客户端来说,则是以不同方式处理。 我能想到的最简单的方法是创build一个主列表,然后让服务器克隆一个新的副本来操作,而不必为每个连接的新客户端更改主列表,因此需要copyObj()

编辑:我可能应该提到这在原来的问题 – 这是运行node.js作为服务器,而不是在浏览器,所以浏览器交叉兼容性不是一个问题。

编辑2:为了copyObj()注释,我将在这里提及它:我使用示例对象对我的copyObj()函数对JSON.parse(JSON.stringify(obj))利用进行了快速基准testing以上。 我的版本似乎运行约JSON方法需要的时间约75%(100万份采取~3.2秒我的和约4.4秒JSON)。 所以这让我感觉更好,花时间写我自己的。

编辑3:在Vitum.us的答案的对象types列表中的工作,我扔在一起copyObj()函数的更新版本。 我还没有广泛的testing,性能比老版本差两倍左右,但我认为它应该适用于所有内置types(假设列表已经完成)。

 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属性,正如Mike所build议的那样,这似乎是在诀窍中。 到目前为止,我已经用RegExpDate对象对它进行了testing,它们都似乎正确地复制。 你们有没有看到任何公然(或微妙)不正确的事情?

检测纯JS对象(使用{}new Object创build)的一种方法是使用jQuery方法jQuery.isPlainObject 。 然而,文档说“ 主机对象有许多不一致性,这些不一致性很难强大地检测跨平台,因此在某些情况下,$ .isPlainObject()可能在不同的浏览器上评估不一致与node.js应该被testing。

编辑:回应你的评论:你可以使用jQuery和node.js,看到这个问题: 我可以使用jQuery的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; } 

您可以使用它来检测对象是否是正则expression式

 Object.prototype.toString.call( regexpObject ) == "[object RegExp]" 

这是在获取对象类的规范中提到的方式。

ECMAScript 5的8.6.2节对象内部属性和方法

本规范针对每种内置对象定义了[[Class]]内部属性的值。 主对象的[[Class]]内部属性的值可以是“Arguments”,“Array”,“Boolean”,“Date”,“Error”,“Function”,“JSON” ,“math”,“数字”,“对象”,“RegExp”和“string” 。 [[Class]]内部属性的值用于内部区分不同types的对象。 请注意,除了通过Object.prototype.toString(见15.2.4.2)外,本规范没有提供任何方法让程序访问该值。

RegExp是第15.10节规范中定义的一类对象RegExp(RegularExpression)对象

RegExp对象包含正则expression式和关联的标志。

然后,您可以使用new RegExp()复制RegExp对象

 var oldObject = /[az]+/; var newObject = new RegExp(oldObject);