我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
我找到了一种用函数克隆对象的方法(打断多行以便于理解):
const clone = Object.assign(
Object.create(
Object.getPrototypeOf(originalObject)
),
dataObject
);
其他回答
好的,假设你有下面的这个对象,你想克隆它:
let obj = {a:1, b:2, c:3}; //ES6
or
var obj = {a:1, b:2, c:3}; //ES5
答案主要取决于您使用的ECMAscript,在ES6+中,您可以简单地使用Object.assign来执行克隆:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
或使用如下扩展运算符:
let cloned = {...obj}; //new {a:1, b:2, c:3};
但是如果你使用ES5,你可以使用很少的方法,但JSON.stringify,只需确保你没有使用大量数据来复制,但在很多情况下,这可能是一种简单的方法,比如:
let cloned = JSON.parse(JSON.stringify(obj));
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:
var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references
obj2.text = 'moo2'; // Only updates obj2's text property
console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
对于当前不支持Object.create的浏览器/引擎,可以使用以下polyfill:
// Polyfill Object.create if it does not exist
if (!Object.create) {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
根据MDN:
如果要浅层复制,请使用Object.assign({},a)对于“深度”复制,请使用JSON.parse(JSON.stringify(a))
不需要外部库,但您需要首先检查浏览器兼容性。
我认为,在没有库的情况下,缓存的重复性是最好的。
被低估的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 x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v
因此,要解决这个问题,请执行以下操作:
let y = JSON.parse(JSON.stringify(x)); //see Note below
防止引用的另一种方法是执行以下操作:
let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)
y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }
注:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone