我在创建对象数组的副本时遇到问题。我无法获得指向新的独立数组的新引用。
function OBJ1(name, tags) {
this.myname = name;
this.mytags = tags;
this.myvalue = 0;
}
function OBJ2(arg1) {
this.arg1 = arg1;
this.myarray = [];
}
var OBJ1_array = [];
var result_array2 = null;
var result;
OBJ1_array = createarray1();
for (i = 0; i < 2; i++) {
result = createarray2();
}
function createarray1() {
var myarray = [];
myarray.push(new OBJ1("NAME", [1, 2, 3]));
myarray.push(new OBJ1("others", [1, 2, 3]));
myarray.push(new OBJ1("total", [1, 2, 3]));
return myarray;
}
function createarray2() {
var newarray = $.extend(true, [], OBJ1_array); // newarray should refer to a new array, not the same one as OBJ1_array
OBJ1_array[0].myname = "CHANGED";
console.log("categories", JSON.parse(JSON.stringify(OBJ1_array)));
console.log("newarray", JSON.parse(JSON.stringify(newarray)));
}
输出:
testscript.js:45 categories (3) [{…}, {…}, {…}]0: {myname: "CHANGED", mytags: Array(3), myvalue: 0}1: {myname: "others", mytags: Array(3), myvalue: 0}2: {myname: "total", mytags: Array(3), myvalue: 0}length: 3__proto__: Array(0)
testscript.js:46 newArray (3) [{…}, {…}, {…}]0: {myname: "CHANGED", mytags: Array(3), myvalue: 0}1: {myname: "others", mytags: Array(3), myvalue: 0}2: {myname: "total", mytags: Array(3), myvalue: 0}length: 3__proto__: Array(0)
我期望OBJ1_array[0].myname="CHANGED";
对新创建的数组newArray
没有影响。我尝试过但没有用的东西:
var newArray = OBJ1_array.map(a => ({...a}));
var newarray=$.extend(true,[],OBJ1_array);
我该如何解决这个问题?
$.extend
文档说明如下:
未复制未定义的属性。但是,从对象原型继承的属性将被复制。作为通过
new MyCustomObject(args)
构建的对象的属性或内置的JavaScript类型(如Date或RegExp)不会重构,并且在结果对象或数组中将显示为普通对象。
这意味着将深入合并/复制其中包含所有普通对象的数组。但是,不会重建使用new
关键字创建的对象。这给我们留下了以下场景:
数组副本工作正常,但由于数组中的元素是使用new
关键字创建的,因此不会进一步合并。当改变数组本身(推,弹等)时,你会发现数组确实是一个副本。
这里的问题是您访问数组中的一个元素并更改对象(使用new
关键字创建)。两个数组仍然指向同一个对象,因此当从另一个包含相同对象引用的数组中读取时,您也会看到此更改。
要解决此问题,您还必须make a copy数组中的每个对象。根据您的使用情况,您可以使用Object.assign
或Object.create
在盲目使用之前查看文档。
我还创建了一个最小的问题示例,让您更好地理解该问题。
// setup
var array1, array2, array3, array4;
function Dummy(name) { this.name = name }
// test #1 - using plain objects
array1 = [{ name: 'Foo' }];
array2 = $.extend(true, [], array1);
array1[0].name = 'Bar';
console.log(array1[0].name, array2[0].name);
// test #2 - using the `new` keyword
array3 = [new Dummy('Foo')];
array4 = $.extend(true, [], array3);
array3[0].name = 'Bar';
console.log(array3[0].name, array4[0].name);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
问题出在你的循环和OBJ1函数中。第一次OBJ1_array没问题但是当你第二次来它的价值已经改变了..
你可以尝试这个代码
function OBJ1(name, tags) {
return {myname:name, tags:tags}
//this.myvalue = 0;
}
function OBJ2(arg1) {
this.arg1 = arg1;
this.myarray = [];
}
var OBJ1_array = [];
var result_array2 = null;
var result;
OBJ1_array = createarray1();
for (i = 0; i < 2; i++) {
let tempArr = $.extend(true, [], OBJ1_array);
result = createarray2();
OBJ1_array = tempArr;
}
function createarray1() {
let myarray = [];
myarray.push(new OBJ1("NAME", [1, 2, 3]));
myarray.push(new OBJ1("others", [1, 2, 3]));
myarray.push(new OBJ1("total", [1, 2, 3]));
return myarray;
}
function createarray2() {
let newarray =$.extend(true, [], OBJ1_array);// newarray should refer to a new array, not the same one as OBJ1_array
OBJ1_array[0].myname = "CHANGED";
console.log("categories", JSON.parse(JSON.stringify(OBJ1_array)));
console.log("newarray", JSON.parse(JSON.stringify(newarray)));
}
更新了答案。实现所需目标的最简单方法是使用JSON.stringify
和JSON.parse
创建对象数组的未链接副本。
const OBJ1 = (name, tags) => ({
myname: name,
mytags: tags,
myvalue: 0,
})
function createarray1() {
var myarray=[];
myarray.push(OBJ1("NAME", [1,2,3]));
myarray.push(OBJ1("others", [1,2,3]));
myarray.push(OBJ1("total", [1,2,3]));
return myarray;
}
const arr = createarray1()
// here you create a copy of array
const newArr = JSON.parse(JSON.stringify(arr))
// apply changes directly to the copy
newArr[0].myname = 'Something else'
console.log(newArr)
console.log(arr)
数组和对象是引用类型,这意味着当您通过赋值创建副本时,您只是复制引用而不是基础数组/对象。在您的情况下,复制数组时,您将复制所有对象引用,这些引用仍将指向原始数组中的对象。您还需要克隆对象才能使其正常工作。
使用Array.map()
迭代您的数组并复制每个项目。
使用Object.create()
对每个对象进行浅层克隆。此函数使用原型和属性描述符来创建新对象。您可以使用Object.getPrototypeOf()
Object.getOwnPropertyDescriptors()
将输入对象的原型和属性描述符传递给它。
function OBJ1(name) {
this.myname = name;
}
const array1 = [new OBJ1("NAME")];
const array2 = array1.map(obj =>
Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
);
array2[0].myname = 'Jack';
console.log(array1[0].myname);
console.log(array2[0].myname);
我认为你需要深入克隆你的对象。请使用以下功能
function clone(src) {
var ret=(src instanceof Array ? [] : {});
for(var key in src)
{
if(!src.hasOwnProperty(key)) { continue; }
var val=src[key];
if(val && typeof(val)=='object') { val=clone(val); }
ret[key]=val;
}
return ret;
}
function OBJ1(name, tags) {
this.myname = name;
this.mytags = tags;
this.myvalue = 0;
}
function OBJ2(arg1) {
this.arg1 = arg1;
this.myarray = [];
}
var OBJ1_array = [];
var result_array2 = null;
var result;
OBJ1_array = createarray1();
for (i = 0; i < 2; i++) {
result = createarray2();
}
function createarray1() {
var myarray = [];
myarray.push(new OBJ1("NAME", [1, 2, 3]));
myarray.push(new OBJ1("others", [1, 2, 3]));
myarray.push(new OBJ1("total", [1, 2, 3]));
return myarray;
}
function createarray2() {
var newarray = clone(OBJ1_array) ; // newarray should refer to a new array, not the same one as OBJ1_array
OBJ1_array[0].myname = "CHANGED";
console.log("categories", JSON.parse(JSON.stringify(OBJ1_array)));
console.log("newarray", JSON.parse(JSON.stringify(newarray)));
}
更简单的方法
var cloneOfOBJ1_array = JSON.parse(JSON.stringify(OBJ1_array));
用Object.assign解决了一系列对象的克隆问题
const newArray = myArray.map(a => Object.assign({}, a));
甚至更短的spread syntax
const newArray = myArray.map(a => ({...a}));