我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
我有一个对象x。我想将其复制为对象y,这样对y的更改不会修改x。我意识到复制从内置JavaScript对象派生的对象会导致额外的、不需要的财产。这不是问题,因为我正在复制我自己的一个文字构造对象。
如何正确克隆JavaScript对象?
当前回答
您可以克隆一个对象,并使用一行代码从上一个对象中删除任何引用。只需执行以下操作:
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();
};
}
其他回答
一个特别不优雅的解决方案是使用JSON编码对没有成员方法的对象进行深度复制。方法是对目标对象进行JSON编码,然后通过对其进行解码,获得所需的副本。您可以解码任意次数,以制作任意数量的副本。
当然,函数不属于JSON,因此这只适用于没有成员方法的对象。
这种方法非常适合我的用例,因为我将JSON blob存储在一个键值存储中,并且当它们在JavaScript API中作为对象公开时,每个对象实际上都包含对象的原始状态的副本,因此我们可以在调用方对公开的对象进行变异后计算增量。
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
由于mindeeavor指出要克隆的对象是一个“字面构造的”对象,因此解决方案可能是简单地多次生成对象,而不是克隆对象的实例:
function createMyObject()
{
var myObject =
{
...
};
return myObject;
}
var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
基于模板克隆对象。如果您不想要精确的副本,但确实想要某种可靠的克隆操作的健壮性,但您只想要克隆位,或者希望确保可以控制每个属性值的存在或格式,该怎么办?
我之所以这么做,是因为它对我们有用,我们之所以创建它,是因为我们找不到类似的东西。您可以使用它来基于模板对象克隆对象,该模板对象指定了要克隆的对象的属性,如果源对象上不存在这些属性,或者您希望如何处理克隆,则模板允许函数将这些属性转换为不同的属性。如果没有用,我相信有人可以删除这个答案。
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
if (typeof cloneConstructor === "undefined") {
cloneConstructor = false;
}
if (obj == null || typeof (obj) != 'object') return obj;
//if we have an array, work through it's contents and apply the template to each item...
if (Array.isArray(obj)) {
var ret = [];
for (var i = 0; i < obj.length; i++) {
ret.push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
}
return ret;
}
//otherwise we have an object...
//var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because typescript defines this, so if we are dealing with a typescript object it might reset values.
var temp = cloneConstructor ? new obj.constructor() : {};
for (var key in tpl) {
//if we are provided with a function to determine the value of this property, call it...
if (isFunction(tpl[key])) {
temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
} else {
//if our object has this property...
if (obj[key] != undefined) {
if (Array.isArray(obj[key])) {
temp[key] = [];
for (var i = 0; i < obj[key].length; i++) {
temp[key].push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
}
} else {
temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
}
}
}
}
return temp;
}
一个简单的调用方法如下:
var source = {
a: "whatever",
b: {
x: "yeah",
y: "haha"
}
};
var template = {
a: true, //we want to clone "a"
b: {
x: true //we want to clone "b.x" too
}
};
var destination = cloneObjectByTemplate(source, template);
如果您想使用函数来确保返回属性或确保它是特定类型,请使用这样的模板。我们提供了一个函数,它仍然只是复制源对象的ID属性,但它确保它是一个数字,即使它不存在于源对象上。
var template = {
ID: function (srcObj) {
if(srcObj.ID == undefined){ return -1; }
return parseInt(srcObj.ID.toString());
}
}
数组可以很好地进行克隆,但如果您愿意,也可以使用自己的函数来处理这些单独的属性,并执行以下特殊操作:
var template = {
tags: function (srcObj) {
var tags = [];
if (process.tags != undefined) {
for (var i = 0; i < process.tags.length; i++) {
tags.push(cloneObjectByTemplate(
srcObj.tags[i],
{ a : true, b : true } //another template for each item in the array
);
}
}
return tags;
}
}
所以在上面,我们的模板只复制源对象的标签属性(如果它存在的话)(假设它是一个数组),并且对该数组中的每个元素调用clone函数,根据第二个模板单独克隆它,该模板只复制这些标签元素的a和b属性。
如果您要将对象移入和移出节点,并且希望控制这些对象的哪些属性被克隆,那么这是在node.js中控制这些属性的一种很好的方法,代码也可以在浏览器中工作。
下面是它的用法示例:http://jsfiddle.net/hjchyLt1/
在ECMAScript 6中有Object.assign方法,它将所有可枚举自身财产的值从一个对象复制到另一个对象。例如:
var x = {myProp: "value"};
var y = Object.assign({}, x);
但请注意,这是一个浅层复制-嵌套对象仍然作为引用复制。
我之所以来到这个页面是因为同样的问题,但我既没有使用JQuery,也没有一个克隆方法适用于我自己的对象。
我知道我的答案与这个问题没有太大关系,因为这是一种不同的方法。我不使用克隆函数,而是使用创建函数。它对我来说有以下(不幸的是限制)目的:
我主要使用JSP生成的Javascript我一开始就知道必须生成哪个Object(在我的例子中,它是来自数据库的信息,只获取一次,需要在JS中更频繁地部署。
首先,我这样定义对象:
var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';
现在我移动了所有的东西,比如:
function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");
var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);
var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);
...
return obj;
}
并调用常规代码中的函数:
myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));
正如所说,这是一种不同的方法,它为我的目的解决了我的问题。