我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
我认为,在没有库的情况下,缓存的重复性是最好的。
被低估的WeakMap涉及到循环的问题,其中存储对新旧对象的引用可以帮助我们很容易地重建整个树。
我阻止了DOM元素的深度克隆,可能您不想克隆整个页面:)
function deepCopy(object) {
const cache = new WeakMap(); // Map of old - new references
function copy(obj) {
if (typeof obj !== 'object' ||
obj === null ||
obj instanceof HTMLElement
)
return obj; // primitive value or HTMLElement
if (obj instanceof Date)
return new Date().setTime(obj.getTime());
if (obj instanceof RegExp)
return new RegExp(obj.source, obj.flags);
if (cache.has(obj))
return cache.get(obj);
const result = obj instanceof Array ? [] : {};
cache.set(obj, result); // store reference to object before the recursive starts
if (obj instanceof Array) {
for(const o of obj) {
result.push(copy(o));
}
return result;
}
const keys = Object.keys(obj);
for (const key of keys)
result[key] = copy(obj[key]);
return result;
}
return copy(object);
}
一些测试:
// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference
var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).
// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true
// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;
注意:我使用常量、for of循环、=>运算符和WeakMaps来创建更重要的代码。当前的浏览器支持此语法(ES6)
其他回答
我认为有一个简单有效的答案。在深度复制中,有两个问题:
保持财产相互独立。并在克隆的对象上保持方法的有效性。
所以我认为一个简单的解决方案是首先序列化和反序列化,然后对其进行赋值以复制函数。
let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);
虽然这个问题有很多答案,但我希望这个问题也能有所帮助。
我认为,在没有库的情况下,缓存的重复性是最好的。
被低估的WeakMap涉及到循环的问题,其中存储对新旧对象的引用可以帮助我们很容易地重建整个树。
我阻止了DOM元素的深度克隆,可能您不想克隆整个页面:)
function deepCopy(object) {
const cache = new WeakMap(); // Map of old - new references
function copy(obj) {
if (typeof obj !== 'object' ||
obj === null ||
obj instanceof HTMLElement
)
return obj; // primitive value or HTMLElement
if (obj instanceof Date)
return new Date().setTime(obj.getTime());
if (obj instanceof RegExp)
return new RegExp(obj.source, obj.flags);
if (cache.has(obj))
return cache.get(obj);
const result = obj instanceof Array ? [] : {};
cache.set(obj, result); // store reference to object before the recursive starts
if (obj instanceof Array) {
for(const o of obj) {
result.push(copy(o));
}
return result;
}
const keys = Object.keys(obj);
for (const key of keys)
result[key] = copy(obj[key]);
return result;
}
return copy(object);
}
一些测试:
// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference
var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).
// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true
// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;
注意:我使用常量、for of循环、=>运算符和WeakMaps来创建更重要的代码。当前的浏览器支持此语法(ES6)
在ES-6中,您可以简单地使用Object.assign(…)。前任:
let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
这里有一个很好的参考:https://googlechrome.github.io/samples/object-assign-es6/
复制对象最正确的方法是使用object.create:
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
这样的符号将使相同的对象具有正确的原型和隐藏的财产。
根据MDN:
如果要浅层复制,请使用Object.assign({},a)对于“深度”复制,请使用JSON.parse(JSON.stringify(a))
不需要外部库,但您需要首先检查浏览器兼容性。