什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。
当前回答
由于这个问题有很多注意力和答案,参考内置的功能,如 Object.assign 或自定义代码到深克隆,我想分享一些图书馆到深克隆,
1、斯克隆
npm 安装 --savedev esclone https://www.npmjs.com/package/esclone
在 ES6 中使用例子:
import esclone from "esclone";
const rockysGrandFather = {
name: "Rockys grand father",
father: "Don't know :("
};
const rockysFather = {
name: "Rockys Father",
father: rockysGrandFather
};
const rocky = {
name: "Rocky",
father: rockysFather
};
const rockyClone = esclone(rocky);
在 ES5 中使用例子:
var esclone = require("esclone")
var foo = new String("abcd")
var fooClone = esclone.default(foo)
console.log(fooClone)
console.log(foo === fooClone)
二、深度复制
npm 安装深复制 https://www.npmjs.com/package/deep-copy
例子:
var dcopy = require('deep-copy')
// deep copy object
var copy = dcopy({a: {b: [{c: 5}]}})
// deep copy array
var copy = dcopy([1, 2, {a: {b: 5}}])
3、克隆深度
$ npm 安装 --save clone-deep https://www.npmjs.com/package/clone-deep
例子:
var cloneDeep = require('clone-deep');
var obj = {a: 'b'};
var arr = [obj];
var copy = cloneDeep(arr);
obj.c = 'd';
console.log(copy);
//=> [{a: 'b'}]
console.log(arr);
其他回答
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});
对于一个微小的副本,有一个很好的,简单的方法引入了ECMAScript2018标准,它涉及使用 Spread Operator :
let obj = {a : "foo", b:"bar" , c:10 , d:true , e:[1,2,3] };
let objClone = { ...obj };
我在Chrome浏览器中测试了它,两个对象都存储在不同的位置,所以即时更改儿童值在一个不会改变另一个。
这个技术是非常简单的,直前的,我认为这是一个真正的最佳实践这个问题一次和永远。
这里是一个全面的克隆()方法,可以克隆任何JavaScript对象,它处理几乎所有情况:
function clone(src, deep) {
var toString = Object.prototype.toString;
if (!src && typeof src != "object") {
// Any non-object (Boolean, String, Number), null, undefined, NaN
return src;
}
// Honor native/custom clone methods
if (src.clone && toString.call(src.clone) == "[object Function]") {
return src.clone(deep);
}
// DOM elements
if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
return src.cloneNode(deep);
}
// Date
if (toString.call(src) == "[object Date]") {
return new Date(src.getTime());
}
// RegExp
if (toString.call(src) == "[object RegExp]") {
return new RegExp(src);
}
// Function
if (toString.call(src) == "[object Function]") {
//Wrap in another method to make sure == is not true;
//Note: Huge performance issue due to closures, comment this :)
return (function(){
src.apply(this, arguments);
});
}
var ret, index;
//Array
if (toString.call(src) == "[object Array]") {
//[].slice(0) would soft clone
ret = src.slice();
if (deep) {
index = ret.length;
while (index--) {
ret[index] = clone(ret[index], true);
}
}
}
//Object
else {
ret = src.constructor ? new src.constructor() : {};
for (var prop in src) {
ret[prop] = deep
? clone(src[prop], true)
: src[prop];
}
}
return ret;
};
这是我创建的最快的方法,不使用原型,所以它将保持在新对象中拥有自己的所有权。
解决方案是对原件的顶级属性进行 iterate,创建两个副本,从原件中删除每个属性,然后重新设置原件并返回新副本,它只需要像顶级属性一样多次 iterate。
唯一的缺点是,原始对象必须配备其原创创建的名称空间,以便重新设置。
copyDeleteAndReset:function(namespace,strObjName){
var obj = namespace[strObjName],
objNew = {},objOrig = {};
for(i in obj){
if(obj.hasOwnProperty(i)){
objNew[i] = objOrig[i] = obj[i];
delete obj[i];
}
}
namespace[strObjName] = objOrig;
return objNew;
}
var namespace = {};
namespace.objOrig = {
'0':{
innerObj:{a:0,b:1,c:2}
}
}
var objNew = copyDeleteAndReset(namespace,'objOrig');
objNew['0'] = 'NEW VALUE';
console.log(objNew['0']) === 'NEW VALUE';
console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};
对于想要使用 JSON.parse(JSON.stringify(obj)) 版本的人,但没有失去日期对象,您可以使用 Parse 方法的第二个论点将行转换为日期:
function clone(obj) { var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; return JSON.parse(JSON.stringify(obj), function(k, v) { if (typeof v === 'string' && regExp.test(v)) return new Date(v) return v; }) } // usage: var original = { a: [1, null, undefined, 0, {a:null}, new Date()], b: { c(){ return 0 } } } var cloned = clone(original) console.log(cloned)