JavaScript 开发者经常觉得“合并”(merge)两个对象很有用。更具体地说,就是把源对象所有的本地属性一起复制到目标对象上。有时候这种操作也被称为“混入”(mixin),因为目标对象通过混入源对象的属性得到了增强。

assign() 浅拷贝

ECMAScript 6 专门为合并对象提供了Object.assign()方法。这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举(Object.propertyIsEnumerable()返回 true)和自有(Object.hasOwnProperty()返回 true)属性复制到目标对象。以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。

  1. let dest, src, result;
  2. /**
  3. * 简单复制
  4. */
  5. dest = {};
  6. src = { id: 'src' };
  7. result = Object.assign(dest, src);
  8. // Object.assign 修改目标对象
  9. // 也会返回修改后的目标对象
  10. console.log(dest === result); // true
  11. console.log(dest !== src); // true
  12. console.log(result); // { id: src }
  13. console.log(dest); // { id: src }
  14. /**
  15. * 多个源对象
  16. */
  17. dest = {};
  18. result = Object.assign(dest, { a: 'foo' }, { b: 'bar' });
  19. console.log(result); // { a: foo, b: bar }
  20. /**
  21. * 获取函数与设置函数
  22. */
  23. dest = {
  24. set a(val) {
  25. console.log(`Invoked dest setter with param ${val}`);
  26. }
  27. };
  28. src = {
  29. get a() {
  30. console.log('Invoked src getter');
  31. return 'foo';
  32. }
  33. };
  34. Object.assign(dest, src);
  35. // 调用 src 的获取方法
  36. // 调用 dest 的设置方法并传入参数"foo"
  37. // 因为这里的设置函数不执行赋值操作
  38. // 所以实际上并没有把值转移过来
  39. console.log(dest); // { set a(val) {...} }

Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。此外,从源对象访问器属性取得的值,比如获取函数,会作为一个静态值赋给目标对象。换句话说,不能在两个对象间转移获取函数和设置函数。

注意 浅拷贝只是在更深层次(比如对象)进行地址传递,而在简单的属性上依然是拷贝

  1. var obj = {
  2. id: 1, //此项修改不会引起拷贝对象改变
  3. name: 'andy', //这一项也一样
  4. msg: {
  5. age: 18 //修改对象里的属性时,拷贝对象只是拷贝对象地址,所以会同时修改
  6. }
  7. };
  8. var o = {};
  9. Object.assign(o, obj);
  10. console.log(o);
  11. o.msg.age = 20; //拷贝的对象公用同一个地址,改变会一起改变
  12. o.id=3; //只是o对象里id属性修改
  13. o.name="some" //与上面相同,只有o对象里属性改变
  14. console.log(obj);

浅拷贝后修改拷贝对象的属性不会影响获得拷贝对象 (当然只是简单属性,不涉及拷贝对象里包含的对象里的属性)


定义一个递归函数来进行深拷贝

  1. function deepCopy(newobj,oldobj) {
  2. for (const k in oldobj) {
  3. let item =oldobj[k];
  4. if (item instanceof Array) {
  5. newobj[k]=[];
  6. deepCopy(newobj[k],item)
  7. }else if(item instanceof Object){
  8. newobj[k]={};
  9. deepCopy(newobj[k],item)
  10. }else{
  11. newobj[k]=item;
  12. }
  13. }
  14. }
  15. //使用函数
  16. obj={
  17. name:'other',
  18. sex:'nan',
  19. num:[1,5,3,4,8,6,[5,4,8,6,2,1]],
  20. score:{
  21. some:0
  22. }
  23. };
  24. o={};
  25. deepCopy(o,obj)
  26. console.log(o);

通过JSON来进行深拷贝

  1. var targetObj = JSON.parse(JSON.stringify(copyObj))
  2. let arr4 = JSON.parse(JSON.stringify(arr))

缺点:
(1)如果对象里有函数,函数无法被拷贝下来
(2)无法拷贝copyObj对象原型链上的属性和方法
(3)当数据的层次很深,会栈溢出