obj = eval(uneval(o));
,但这是非标准的,只有Firefox支持。我已经尝试过像
obj = JSON.parse(JSON.stringify(o));
这样的方式,但质疑其效率。我也看到了递归复制函数的各种缺陷。
我很惊讶没有一个权威的解决方案存在。
obj = eval(uneval(o));
,但这是非标准的,只有Firefox支持。obj = JSON.parse(JSON.stringify(o));
这样的方式,但质疑其效率。浅拷贝的一行代码 (ECMAScript 第五版):
var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
浅拷贝一行代码 (ECMAScript第六版,2015年):
var origin = { foo : {} };
var copy = Object.assign({}, origin);
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
目前似乎还没有针对类数组对象的理想深度克隆操作符。如下代码所示,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)
我没有看到提到 AngularJS,因此认为人们可能想知道...
angular.copy
还提供了一种深拷贝对象和数组的方法。
angular.extend({},obj);
- GalvanijQuery.extend
和angular.extend
都是浅拷贝。而angular.copy
则是深拷贝。 - Dan Atkinson只有当您可以使用ECMAScript 6或转译器时才能实现。
特点:
代码:
function clone(target, source){
for(let key in source){
// Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
let descriptor = Object.getOwnPropertyDescriptor(source, key);
if(descriptor.value instanceof String){
target[key] = new String(descriptor.value);
}
else if(descriptor.value instanceof Array){
target[key] = clone([], descriptor.value);
}
else if(descriptor.value instanceof Object){
let prototype = Reflect.getPrototypeOf(descriptor.value);
let cloneObject = clone({}, descriptor.value);
Reflect.setPrototypeOf(cloneObject, prototype);
target[key] = cloneObject;
}
else {
Object.defineProperty(target, key, descriptor);
}
}
let prototype = Reflect.getPrototypeOf(source);
Reflect.setPrototypeOf(target, prototype);
return target;
}
Date
这样的数据类型存在问题。 - ZortextObject.create(null) instanceof Object
为false。 - CherryDT我晚回答了这个问题,但是我有另一种复制对象的方法:
function cloneObject(obj) {
if (obj === null || typeof(obj) !== 'object')
return obj;
var temp = obj.constructor(); // changed
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActiveClone'] = null;
temp[key] = cloneObject(obj[key]);
delete obj['isActiveClone'];
}
}
return temp;
}
var b = cloneObject({"a":1,"b":2}); // calling
这比以下方式更好、更快:
var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));
和
var a = {"a":1,"b":2};
// Deep copy
var newObject = jQuery.extend(true, {}, a);
我已经对代码进行了基准测试,您可以在这里测试结果:
并分享结果: 参考资料:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
obj['isActiveClone'] = null
然后再将其删除?
而且为什么不调用 obj.hasOwnProperty(key)
? - Aykut Kllic根据你的目标是否是克隆“普通 JavaScript 对象”,我有两个好的答案。
假设你的意图是创建一个完整的副本,不包含指向源对象的原型引用。如果您不需要完整的克隆,则可以使用其他答案中提供的许多 Object.clone()例程(Crockford 的模式)。
对于普通的 JavaScript 对象,在现代运行时中克隆对象的一种经过验证的好方法是:
var clone = JSON.parse(JSON.stringify(obj));
请注意,源对象必须是一个纯JSON对象。也就是说,它的所有嵌套属性都必须是标量(如布尔值、字符串、数组、对象等)。任何函数或特殊对象,如RegExp或Date都不会被克隆。
这个方法非常有效率。我们尝试了各种克隆方法,这个方法效果最好。我相信有些高手可以想出更快的方法,但我怀疑这只是微小的提升。
这种方法简单易行,易于实现。将其包装成方便的函数,如果您确实需要挤出一些效果,在以后的某个时候再进行优化即可。
对于非纯JavaScript对象,没有一个真正简单的答案。事实上,由于JavaScript函数和内部对象状态的动态性质,这是不可能的。深度克隆具有内部函数的JSON结构需要重新创建这些函数及其内部上下文。而JavaScript根本没有标准化的处理方式。
再次强调,正确的做法是通过声明并重复使用方便的方法来完成。该方便方法可以具备一些关于您自己对象的理解,以便在新对象中正确地重新创建图形。
我们编写了自己的代码,但我看到的最佳通用方法是在这里:
http://davidwalsh.name/javascript-clone
这是正确的想法。作者(David Walsh)已经注释掉了一般化函数的克隆。这取决于您的用例,您可能会选择这样做。
主要思路是您需要根据每种类型特殊处理函数的实例化(或者类原型,可以这么说)。在这里,他提供了一些RegExp和Date的示例。
这段代码不仅简短,而且可读性非常好,很容易扩展。
这有效率吗?当然!既然目标是生成真正的深拷贝克隆,那么您必须遍历源对象图的成员。通过这种方式,您可以微调要处理的子成员以及如何手动处理自定义类型。
两种方法都很有效率。
这通常不是最高效的解决方案,但它能满足我的需求。以下是简单的测试案例...
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
对于想要使用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)
以下是快速参考的函数:
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
}
if(o instanceof Date) return new Date(o.valueOf());
的内容。 - Luis// 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;
};
eval()
通常是不明智的,因为许多JavaScript引擎的优化器必须在处理通过eval()
设置的变量时关闭。仅仅使用eval()
就可能导致代码性能更差。 - user56reinstatemonica8JSON
方法会丢失任何在JSON中没有等价的JavaScript类型。例如:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
将生成{a: null, b: null, c: null, g: false}
。 - oriadam