由JS在对象数组循环过程中修改对象属性所导致的问题谈到对象的浅拷贝和深拷贝

最近在工作中用React.js写前端,接触了很多JS的东东,在做数组嵌套循环的时候发现一个问题:

我的需求是想把arr1和arr2添加到resultArr 中,并且给arr2分别添加index属性为arr1的idnex值。代码如下:

const arr1 = [{ id: 1, name: 'a', index: 0 }, { id: 2, name: 'b', index: 1 }];
const arr2 = [{ id: 3, name: 'c' }, { id: 4, name: 'd' }];

const resultArr = [];
arr1.forEach((itemA, index) => {
	resultArr.push(itemA);
	arr2.forEach((itemB) => {
    	const item = itemB;
    	item.index = index;
    	resultArr.push(item);
  	});
});

console.log('resultArr: ',resultArr );
    /*期望结果:
	    0: {id: 1, name: "a", index: 0}
	    1: {id: 3, name: "c", index: 0}
	    2: {id: 4, name: "d", index: 0}
	    3: {id: 2, name: "b", index: 1}
	    4: {id: 3, name: "c", index: 1}
	    5: {id: 4, name: "d", index: 1}
    */
    /*实际结果:
     	0: {id: 1, name: "a", index: 0}
	    1: {id: 3, name: "c", index: 1}
	    2: {id: 4, name: "d", index: 1}
	    3: {id: 2, name: "b", index: 1}
	    4: {id: 3, name: "c", index: 1}
	    5: {id: 4, name: "d", index: 1}
    */

刚开始看到这个确实比较懵逼,在网上冲浪(划水)的间隙查了一下,应该是在arr2循环中改变了arr1数组对象的属性导致的。

改为以下代码就没有这个问题了。

const arr1 = [{ id: 1, name: 'a', index: 0 }, { id: 2, name: 'b', index: 1 }];
const arr2 = [{ id: 3, name: 'c' }, { id: 4, name: 'd' }];

const resultArr = [];
arr1.forEach((itemA, index) => {
	resultArr.push(itemA);
    arr2.forEach((itemB) => {
    	const item = {
        	id: itemB.id,
        	name: itemB.name,
        	index: index
        };
        resultArr.push(item);
    });
});

console.log(resultArr);

知其然,更应知其所以然(这样才有利于更好的装逼),很明显这里两个写法唯一的不同就是数组的浅拷贝和深拷贝了,那么接下来就说说js的浅拷贝和深拷贝吧!

浅拷贝是拷贝一层,深层次的对象就拷贝其引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;

   const obj1 = {
      a: 'hello',
      b: 18,
      c: {
        name: '如花',
        weight: 200
      }
    };
    const obj2 = Object.assign({}, obj1);
    obj2.b = 30;
    obj2.c.name = '凤姐';
	//结果如下:
    console.log('obj1', obj1);//a:'hello',b:18,c:{name:'凤姐',weight:200}
    console.log('obj2', obj2);//a:'hello',b:30,c:{name:'凤姐',weight:200}
    
    //在copy这个对象的时候,属性a和属性b是直接copy它的值,而属性c也是一个对象,所以copy的是其引用地址

常用的assign(),concat()等方法都是浅拷贝,另外需要注意以下写法:

 const obj1 = {
      a: 'hello',
      b: 18,
      c: {
        name: '如花',
        weight: 200
      }
 };
 const obj2 = obj1;
 obj2.b = 30;
 obj2.c.name = '凤姐';
 //此时是直接进行的地址引用,对obj2的修改都将影响到arr1
 console.log('obj1', obj1);//a:'hello',b:30,c:{name:'凤姐',weight:200}
 console.log('obj2', obj2);//a:'hello',b:30,c:{name:'凤姐',weight:200}

那么如何实现深拷贝呢?
最直接的办法就是挨个属性赋值:

const obj1 = {
      a: 'hello',
      b: 18,
      c: {
        name: '如花',
      }
};
const obj2 = {
	a: obj1.a,
    b: obj1.b,
    c: {
    	name: obj1.c.name,
    }
};
obj2.b = 30;
obj2.c.name = '凤姐';

console.log('obj1', obj1);//a:'hello',b:18,c:{name:'如花',weight:200}
console.log('obj2', obj2);//a:'hello',b:30,c:{name:'凤姐',weight:200}

此外还可以先将a对象转为json,再将json赋值给b对象,不过这两种方法总归没那么优雅,其实用ES6语法写起来倒也不算复杂。

作为一名后端工程师,暂时就了解到这里了,最后用后端习惯的表达方式来总结一下浅拷贝和深拷贝:

在对一个对象进行拷贝时,如果其属性是基本类型(即 Boolean,null,undefined,String 和 Number),则是进行的值拷贝,而如果其属性是Array,Function或Object,则拷贝的是其引用地址,在改变拷贝后的对象中的该属性时,会直接修改该引用所实际指向的地址的值,即会改变原对象该属性的值。

你可能感兴趣的:(React,javascript)