JavaScript深拷贝与浅拷贝

浅拷贝

定义

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。基本类型拷贝的是值,而引用类型拷贝的是地址,且只能拷贝一层对象,假如存在对象的嵌套,那么浅拷贝就无能为力。

浅拷贝方法

  1. object.assign
  2. 扩展运算符let cloneObj = {…obj}
  3. concat运算符
  4. slice拷贝数组
  5. 手工实现

方法一:object.assign
可以理解为Object.assign循环遍历原对象的属性,通过复制的方式将其赋值给目标对象的相应属性,拷贝的是对象的属性的引用,而不是对象本身
语法:

Object.assign(target,sources)

示例代码

let target={};
let source={a: {b:1}};
Object.assign(target,source);
console.log(target);    //{a:{b:10}}
source.a.b = 10;
console.log(source);    //{a:{b:10}}
console.log(target);    //{a:{b:10}}

但是使用object.assign方法有几点需要注意:

  • 它不会拷贝对象的继承属性
  • 它不会拷贝对象的不可枚举的属性
  • 可以拷贝Symbol类型的属性

方法二:扩展运算符
语法:

let cloneObj ={…obj};

示例代码

/*对象的拷贝*/
let obj = {a:1,b:{c:1}};
let obj2 = {...obj};
obj.a = 2;
console.log(obj);  //{a:2,b:{c:1}}
obj.b.c = 2;
console.log(obj); //{a:2,b:{c:1}}

方法三:concat拷贝数组
仅针对数组类型

let arr2 = [1,2,3,4];
let newArr2 = arr2.concat();
newArr[1] = 100;
console.log(arr);  //[1,2,3]
console.log(newArr);   //[1,100,3]

方法四:slice拷贝数组
仅针对数组类型

let arr=[1,2,{val:4}];
let newArr=arr.slice();
newArr[2].val = 1000;
console.log(arr);

手工实现
思路:

  • 对基础类型做最基础的拷贝
  • 对引用类型开辟一个新的存储,并且拷贝一层对象属性
const shallowClone=(target)=>{
  if(typeof target === 'object' && target !== null{
       const cloneTarget = Array.isArray(target)?[]:{};
       for(let prop in target){
         if(target.hasOwnProperty(prop){
            cloneTarget[prop] = target[prop];
         }
       }
       return cloneTarget;
       }else{
       return target;
       }
}

深拷贝

浅拷贝只是创建了一个新的对象,复制了原有对象的基本类型的值,,而引用类型只拷贝了一层属性,再深就无法拷贝,但深拷贝是在堆内存中完全开辟了一块内存地址,并将原有的对象复制过来存放,且新对象的修改不会改变原来的对象。
方法一:JSON.stringfy
把一个对象序列化成JSON的字符串,并将对象里的内容转换成字符串,最后再用JSON.parse()将JSON字符串生成一个新的对象
示例代码

let obj1 = {a:1,b:[1,2,3]}
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log(obj2);  //{a:1,{b:[1,2,3}}
obj1.a = 2;
console.log(obj1);  //{a:2,b:[1,2,3,4]
console.log(obj2);//{a:1,{b:[1,2,3}}                           

但是这只能应付普通的深拷贝,还有以下缺陷:

  • 函数、undefined、symbol不能拷贝
  • 拷贝date类型会变成字符串
  • 无法拷贝原型链
  • 拷贝正则会变成空对象
  • 对象中含有NaN,Infinity,JSON会转化成null
  • 无法拷贝对象的循环应用

手写递归实现

onst isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) 
  return new Date(obj)       // 日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp)
  return new RegExp(obj)     //正则对象直接返回一个新的正则对象
  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  //遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) { 
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }
  return cloneObj
}
// 下面是验证代码
let obj = {
  num: 0,
  str: '',
  boolean: true,
  unf: undefined,
  nul: null,
  obj: { name: '我是一个对象', id: 1 },
  arr: [0, 1, 2],
  func: function () { console.log('我是一个函数') },
  date: new Date(0),
  reg: new RegExp('/我是一个正则/ig'),
  [Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
  enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj    // 设置loop成循环引用的属性
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值