js深拷贝浅拷贝总结


学习前端也有一段时间,最近会把一些面试常遇到的简单题总结下来,整合到脑子里~~~,在这记录一下,大家一起加油吧。

深拷贝,浅拷贝原理

js是没有引用传递的,只有值传递。但是js的数据类型分为基本数据类型(number,null,undefined,string)和引用数据类型(object,function)。基本数据类型的复制大家肯定没有什么异议,但是对于引用数据类型来说,我们可以这样理解,他在内存中存储的是数据存放的地址,而不是数据的值,真正数据存储的值是放在堆内存中。下面放一张盗图,哈哈哈:
基础类型复制
引用类型复制
下面我来解释一下,第一张图来说,一个个的小格格就是栈存储的表示方法,存放的是数据的值,每复制一个值,就在栈空间中再申请一个空间,加入值,即为复制成功;对于第二张图来说,以一个小格格中存放是的值的地址,每复制一个值,就在栈空间中申请一个新的空间,放入需要复制的值的地址,而这个地址和原始地址都指向相同的堆空间,所以可以理解的是,一个值变化,另一个值肯定也会跟着变啦~~~

简单理解

浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制;

浅拷贝

方法一(遍历)

function simpleCopy(obj1){
  var obj2 = Array.isArray(obj1)?[]:{};
  for(let i in obj1){
    obj2[i] = obj1[i];
  }
  return obj2;
}
//测试
var obj1 = [1,2,'3',{'4':4}]
var obj2 = simpleCopy(obj1)
console.log(obj1)
console.log(obj2)

obj1[3][4] = 5;
console.log(obj1)
console.log(obj2)

执行结果:

[ 1, 2, '3', { '4': 4 } ]
[ 1, 2, '3', { '4': 4 } ]
[ 1, 2, '3', { '4': 5 } ]
[ 1, 2, '3', { '4': 5 } ]

发现obj1的第三项和obj2的第三项是引用传递的。

方法二(object.assign)

function simpleCopy2(obj1){
  var obj2 = Object.assign(obj1);
  return obj2;
}
//测试
var obj1 = [1,2,'3',{'4':4}]
var obj2 = simpleCopy(obj1)
console.log(obj1)
console.log(obj2)

obj1[3][4] = 5;
console.log(obj1)
console.log(obj2)

执行结果:

[ 1, 2, '3', { '4': 4 } ]
[ 1, 2, '3', { '4': 4 } ]
[ 1, 2, '3', { '4': 5 } ]
[ 1, 2, '3', { '4': 5 } ]

深拷贝

方法一(JSON)

function deepClone(obj1){
  return JSON.parse(JSON.stringify(obj1));
}
//测试
var obj1 = [1,2,'3',{'4':4},[1,2,3,[4,5]],"",{},NaN, undefined, null, true, function(){return 1}];

var obj2 = deepClone(obj1)
console.log(obj1)
console.log(obj2)

执行结果

[
  1,
  2,
  '3',
  { '4': 4 },
  [ 1, 2, 3, [ 4, 5 ] ],
  '',
  {},
  NaN,
  undefined,
  null,
  true,
  [Function]
]
[
  1,
  2,
  '3',
  { '4': 4 },
  [ 1, 2, 3, [ 4, 5 ] ],
  '',
  {},
  null,
  null,
  null,
  true,
  null
]

通过结果可以看出undefined,function,NaN在json的转化方法直接转换为null。

因为JSON.stringify()将值转换为相应的JSON格式时:(摘自MDN)

  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略( 在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined。
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理。
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null。

方法二:递归

function deepClone1(obj1){
  var obj2 = Array.isArray(obj1)?[]:{};
  if(obj1 && typeof obj1 === "object"){
    for(var i in obj1){
      if(obj1.hasOwnProperty(i)){
        if(obj1[i] && typeof obj1[i] === "object"){
          obj2[i] = deepClone1(obj1[i]);
        }else{
          obj2[i] = obj1[i];
        }
      }
    }
  }
  return obj2;
}
//测试
var obj1 = [1,2,'3',{'4':4},[1,2,3,[4,5]],"",{},NaN, undefined, null, true, function(){return 1}];

var obj2 = deepClone1(obj1)
console.log(obj1)
console.log(obj2)

执行结果

[
  1,
  2,
  '3',
  { '4': 4 },
  [ 1, 2, 3, [ 4, 5 ] ],
  '',
  {},
  NaN,
  undefined,
  null,
  true,
  [Function]
]
[
  1,
  2,
  '3',
  { '4': 4 },
  [ 1, 2, 3, [ 4, 5 ] ],
  '',
  {},
  NaN,
  undefined,
  null,
  true,
  [Function]
]

结果正确。
缺陷:当遇到两个互相引用的对象,会出现死循环的情况,为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

方法三: 方法二的改进

function deepClone2(obj1){
  var obj2 = Array.isArray(obj1)?[]:{};
  if(obj1 && typeof obj1 === "object"){
    for(var i in obj1){
      var prop = obj1[i]
      if(prop == obj1){
        // console.log("prop:",prop)
        obj2[i] = prop;//这个不知道加不加?
        continue;
      }
      if(obj1.hasOwnProperty(i)){
        if(obj1[i] && typeof obj1[i] === "object"){
          obj2[i] = (prop.constructor === Array) ? [] : {};
          arguments.callee(prop, obj2[i]); // 递归调用
        }else{
          obj2[i] = obj1[i];
        }
      }
    }
  }
  return obj2;
}
//测试
var obj1 = [1,2,'3',{'4':4},[1,2,3,[4,5]],"",{},NaN, undefined, null, true, function(){return 1}];
obj1['a'] = obj1; 

var obj2 = deepClone2(obj1)
console.log(obj1)
console.log(obj2)

输出结果

[
  1,
  2,
  '3',
  { '4': 4 },
  [ 1, 2, 3, [ 4, 5 ] ],
  '',
  {},
  NaN,
  undefined,
  null,
  true,
  [Function],
  a: [Circular]
]
[
  1,
  2,
  '3',
  {},
  [],
  '',
  {},
  NaN,
  undefined,
  null,
  true,
  [Function],
  a: [
    1,
    2,
    '3',
    { '4': 4 },
    [ 1, 2, 3, [Array] ],
    '',
    {},
    NaN,
    undefined,
    null,
    true,
    [Function],
    a: [Circular]
  ]
]

好啦,到这里就结束啦。有什么需要可以再来问我哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值