在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)

