我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
我已经编写了自己的实现。不确定它是否算作更好的解决方案:
/*
a function for deep cloning objects that contains other nested objects and circular structures.
objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
index (z)
|
|
|
|
|
| depth (x)
|_ _ _ _ _ _ _ _ _ _ _ _
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/...../
/................./
/..... /
/ /
/------------------
object length (y) /
*/
实施情况如下:
function deepClone(obj) {
var depth = -1;
var arr = [];
return clone(obj, arr, depth);
}
/**
*
* @param obj source object
* @param arr 3D array to store the references to objects
* @param depth depth of the current object relative to the passed 'obj'
* @returns {*}
*/
function clone(obj, arr, depth){
if (typeof obj !== "object") {
return obj;
}
var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'
var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
if(result instanceof Array){
result.length = length;
}
depth++; // depth is increased because we entered an object here
arr[depth] = []; // this is the x-axis, each index here is the depth
arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
// start the depth at current and go down, cyclic structures won't form on depths more than the current one
for(var x = depth; x >= 0; x--){
// loop only if the array at this depth and length already have elements
if(arr[x][length]){
for(var index = 0; index < arr[x][length].length; index++){
if(obj === arr[x][length][index]){
return obj;
}
}
}
}
arr[depth][length].push(obj); // store the object in the array at the current depth and length
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
}
return result;
}
其他回答
您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:
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();
};
}
如果您正在使用TypeScript,需要支持较旧的web浏览器(因此无法使用Object.assign),并且没有使用内置有克隆方法的库,那么您可以在几行代码中使自己成为组合助手。它结合了对象,如果只有一个,就克隆它。
/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
const combined = {};
objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
return combined;
}
您可以使用函数闭包来获得深度复制的所有好处,而无需深度复制。这是一个非常不同的范例,但效果很好。不要试图复制现有对象,只要在需要时使用函数实例化新对象即可。
首先,创建一个返回对象的函数
function template() {
return {
values: [1, 2, 3],
nest: {x: {a: "a", b: "b"}, y: 100}
};
}
然后创建一个简单的浅层复制函数
function copy(a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
}
创建一个新对象,并将模板的财产复制到其中
var newObject = {};
copy(newObject, template());
但上述复制步骤不是必需的。您需要做的就是:
var newObject = template();
现在您有了一个新对象,请测试它的财产:
console.log(Object.keys(newObject));
这将显示:
["values", "nest"]
是的,这些是newObject自己的财产,而不是对另一个对象上财产的引用。让我们检查一下:
console.log(newObject.nest.x.b);
这将显示:
"b"
newObject获得了模板对象的所有财产,但没有任何依赖链。
http://jsbin.com/ISUTIpoC/1/edit?js安慰
我添加此示例是为了鼓励一些讨论,因此请添加一些评论:)
Jan Turo的上述答案非常接近,由于兼容性问题,可能是在浏览器中使用的最佳选择,但这可能会导致一些奇怪的枚举问题。例如,执行:
for ( var i in someArray ) { ... }
在遍历数组元素后,将clone()方法赋给i。下面是一个避免枚举并适用于node.js的改编:
Object.defineProperty( Object.prototype, "clone", {
value: function() {
if ( this.cloneNode )
{
return this.cloneNode( true );
}
var copy = this instanceof Array ? [] : {};
for( var attr in this )
{
if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
{
copy[ attr ] = this[ attr ];
}
else if ( this[ attr ] == this )
{
copy[ attr ] = copy;
}
else
{
copy[ attr ] = this[ attr ].clone();
}
}
return copy;
}
});
Object.defineProperty( Date.prototype, "clone", {
value: function() {
var copy = new Date();
copy.setTime( this.getTime() );
return copy;
}
});
Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );
这避免了使clone()方法可枚举,因为defineProperty()默认为false。
在ES-6中,您可以简单地使用Object.assign(…)。前任:
let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
这里有一个很好的参考:https://googlechrome.github.io/samples/object-assign-es6/