什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。
当前回答
A Recursive Deep Clone 比 JSON.parse(JSON.stringify(obj)) 提到的更快。
Jsperf 在这里排名第一: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5 Jsben 从上面的答案更新显示,一个重复的深度克隆打击所有其他提到的: http://jsben.ch/13YKQ
下面是快速参考的功能:
function cloneDeep (o) {
let newO
let i
if (typeof o !== 'object') return o
if (!o) return o
if (Object.prototype.toString.apply(o) === '[object Array]') {
newO = []
for (i = 0; i < o.length; i += 1) {
newO[i] = cloneDeep(o[i])
}
return newO
}
newO = {}
for (i in o) {
if (o.hasOwnProperty(i)) {
newO[i] = cloneDeep(o[i])
}
}
return newO
}
其他回答
这通常不是最有效的解决方案,但它是我需要的。
function clone(obj, clones) {
// Makes a deep copy of 'obj'. Handles cyclic structures by
// tracking cloned obj's in the 'clones' parameter. Functions
// are included, but not cloned. Functions members are cloned.
var new_obj,
already_cloned,
t = typeof obj,
i = 0,
l,
pair;
clones = clones || [];
if (obj === null) {
return obj;
}
if (t === "object" || t === "function") {
// check to see if we've already cloned obj
for (i = 0, l = clones.length; i < l; i++) {
pair = clones[i];
if (pair[0] === obj) {
already_cloned = pair[1];
break;
}
}
if (already_cloned) {
return already_cloned;
} else {
if (t === "object") { // create new object
new_obj = new obj.constructor();
} else { // Just use functions as is
new_obj = obj;
}
clones.push([obj, new_obj]); // keep track of objects we've cloned
for (key in obj) { // clone object members
if (obj.hasOwnProperty(key)) {
new_obj[key] = clone(obj[key], clones);
}
}
}
}
return new_obj || obj;
}
自行车测试...
a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true
功能测试...
f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
希望这能帮助。
function deepClone(obj) {
/*
* Duplicates an object
*/
var ret = null;
if (obj !== Object(obj)) { // primitive types
return obj;
}
if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { // string objecs
ret = obj; // for ex: obj = new String("Spidergap")
} else if (obj instanceof Date) { // date
ret = new obj.constructor();
} else
ret = Object.create(obj.constructor.prototype);
var prop = null;
var allProps = Object.getOwnPropertyNames(obj); //gets non enumerables also
var props = {};
for (var i in allProps) {
prop = allProps[i];
props[prop] = false;
}
for (i in obj) {
props[i] = i;
}
//now props contain both enums and non enums
var propDescriptor = null;
var newPropVal = null; // value of the property in new object
for (i in props) {
prop = obj[i];
propDescriptor = Object.getOwnPropertyDescriptor(obj, i);
if (Array.isArray(prop)) { //not backward compatible
prop = prop.slice(); // to copy the array
} else
if (prop instanceof Date == true) {
prop = new prop.constructor();
} else
if (prop instanceof Object == true) {
if (prop instanceof Function == true) { // function
if (!Function.prototype.clone) {
Function.prototype.clone = function() {
var that = this;
var temp = function tmp() {
return that.apply(this, arguments);
};
for (var ky in this) {
temp[ky] = this[ky];
}
return temp;
}
}
prop = prop.clone();
} else // normal object
{
prop = deepClone(prop);
}
}
newPropVal = {
value: prop
};
if (propDescriptor) {
/*
* If property descriptors are there, they must be copied
*/
newPropVal.enumerable = propDescriptor.enumerable;
newPropVal.writable = propDescriptor.writable;
}
if (!ret.hasOwnProperty(i)) // when String or other predefined objects
Object.defineProperty(ret, i, newPropVal); // non enumerable
}
return ret;
}
HTTPS://github.com/jinujd/Javascript-Deep-Clone
Lodash 有一个可爱的 _.cloneDeep(值) 方法:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
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});
扩展操作器... (原始序列 - 仅) 序列(0) (原始序列 - 仅) 序列() (原始序列 - 仅) concat() (原始序列 - 仅) 定制功能,如下所示(每个序列) jQuery 的 $.extend() (每个序列) JSON.parse(JSON.stringify()) (原始和字面序列 - 仅) Underscore 的 _.clone() (原始和字面序列 - 仅) Lodash 的 _.cloneDeep() (每个序列)
let arr1a = [1, 'a', true];
let arr1b = [...arr1a];
而且在哪裡 slice() 比 concat( 有更好的性能: https://jsbench.me/x5ktn7o94d/
let arr1c = arr1a.splice(0);
let arr1d = arr1a.slice();
let arr1e = arr1a.concat();
let arr2a = [1, 'a', true, {}, []];
let arr2b = JSON.parse(JSON.stringify(arr2a));
let arr3a = [1, 'a', true, {}, [], new Object()];
function copy(aObject) {
// Prevent undefined objects
// if (!aObject) return aObject;
let bObject = Array.isArray(aObject) ? [] : {};
let value;
for (const key in aObject) {
// Prevent self-references to parent object
// if (Object.is(aObject[key], aObject)) continue;
value = aObject[key];
bObject[key] = (typeof value === "object") ? copy(value) : value;
}
return bObject;
}
let arr3b = copy(arr3a);
或使用第三方实用功能:
let arr3c = $.extend(true, [], arr3a); // jQuery Extend
let arr3d = _.cloneDeep(arr3a); // Lodash
注意: jQuery 的 $.extend 也比 JSON.parse(JSON.stringify() 表现更好):