在JavaScript中深度克隆对象的最有效方法是什么?
时间:2020-03-06 14:36:32 来源:igfitidea点击:
克隆JavaScript对象的最有效方法是什么?我已经看到使用了obj = eval(uneval(o));,但这是非标准的,仅受Firefox支持。
我已经做了类似obj = JSON.parse(JSON.stringify(o));
的事情,但是对效率提出了质疑。
我还看到了具有各种缺陷的递归复制功能。
我感到惊讶的是,不存在规范的解决方案。
解决方案
如果没有内置的,可以尝试:
function clone(obj) { if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; }
function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; }
Note: This is a reply to another answer, not a proper response to this question. If you wish to have fast object cloning please follow Corban's advice in their answer to this question.
我想指出,jQuery中的.clone()方法仅克隆DOM元素。为了克隆JavaScript对象,我们可以执行以下操作:
// Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject);
可以在jQuery文档中找到更多信息。
我还想指出的是,深层副本实际上比上面显示的要聪明得多,它可以避免很多陷阱(例如,尝试深层扩展DOM元素)。它在jQuery核心和插件中经常使用,效果很好。
代码:
// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object && from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; }
测试:
var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj);
这就是我正在使用的:
function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object" && obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; }
var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
// obj target object, vals source object var setVals = function (obj, vals) { if (obj && vals) { for (var x in vals) { if (vals.hasOwnProperty(x)) { if (obj[x] && typeof vals[x] === 'object') { obj[x] = setVals(obj[x], vals[x]); } else { obj[x] = vals[x]; } } } } return obj; };
Crockford建议(并且我更喜欢)使用此功能:
function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject);
它很简洁,可以按预期工作,我们不需要库。
编辑:
这是Object.create的polyfill,因此我们也可以使用它。
var newObject = Object.create(oldObject);
注意:如果我们使用其中的一些,则使用hasOwnProperty的某些迭代可能会遇到问题。因为create
会创建一个继承了oldObject`的新的空对象。但是它对于克隆对象仍然有用且实用。
例如,如果oldObject.a = 5;
newObject.a; // is 5
但:
oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false
对于类似数组的对象,似乎还没有理想的深度克隆运算符。如下代码所示,John Resig的jQuery克隆器将具有非数字属性的数组转换为非数组对象,而RegDwight的JSON克隆器删除了非数字属性。以下测试在多个浏览器上说明了这些要点:
function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names)