JS中对象/数组的深浅拷贝

JS中对象/数组的深浅拷贝

// 对象(数组)的深克隆和浅克隆
 var obj = {
  a: 100,
  b: [10,20,30],
  c: {
    x:10
  },
  d: /^\d+s/,
  e: null,
  f: undefined,
  g: function(){},
  h: new Date(),
  i: Symbol('A'),
};
let arr = [10, [100,200], {x: 10, y: 20}]; 

// 浅克隆:只是最外面的对象obj本身被克隆了一份,但是对象里面的引用数据类型值,还是共用的,一个改了,另一个也会跟着变
// 方案一:使用ES6中的展开运算符
 let newObj = {...obj}; 

// 方案二:使用 Object.assgin来实现浅克隆
// Object.assgin(obj1, obj2):将obj2合并到obj1上,并返回obj1
 let newObj2 = Object.assign({}, obj); 

// 方案三:用 for in 循环
// for in循环在遍历对象的时候,遍历的是当前对象课枚举(列举)的属性
//  + 私有属性(出一下特殊的内置属性是不可枚举的,比如数组的length属性)
// + 公有属性(大部分都是不可枚举的,但是自己在原型上扩展的一般是可枚举的)
// + ...
// 也说明了在遍历的过程中,很有可能会遍历到公有的属性/方法,所以使用for in 循环的时候需要判断属性是否为私有
let newObj3 = {};
for(let key in obj) {
  if(!obj.hasOwnProperty(key)) break;
  newObj3[key] = obj[key];
} 

// console.log(obj, newObj);  // 两个对象长得一样
// console.log(obj === newObj); // false  是两个不同的堆地址
// console.log(obj.b === newObj.b);  // true 对象中的引用类型堆地址一样
// newObj.b.push(40);
// console.log(obj.b); // [10,20,30,40]
// 数组浅克隆
// 方法一:展开运算符
 let newArr = [...arr]; 

// 方案二:使用Object.assgin
let newArr2 = Object.assign([], arr); 

// 方案三:各种循环的迭代
 let newArr3 = arr.map(item => item);
  newArr3 = arr.slice();
  newArr3 = arr.concat([]); 

// console.log(arr, newArr);  // 长得一样
// console.log(arr === newArr);  // false
// console.log(arr[1] === newArr[1]);  // true
// 深克隆:不仅把最外面的对象克隆一份,同时还把对象中的引用数据类型的值也克隆一份,即所有引用类型值克隆之后是独立的(堆地址不同),改了其中一个,不会影响另外一个

// 方案一:先把对象/数组整体变为字符串,再把字符串变为对象/数组
//  对象变为字符串:JSON.stringify([obj])
//  把json字符串转为对象:JSON.parse([string])
 let newArr = JSON.parse(JSON.stringify(arr));  // 深拷贝数组

console.log(arr, newArr); // 长得一样
console.log(arr === newArr); // false
console.log(arr[1] === newArr[1]); // false
newArr[1].push(99);
console.log(arr[1], newArr[1]);  // [100, 200]  [100,200,99]
// 深拷贝对象
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj, newObj); 
/* 
 * 进行拷贝时有问题:
    这种办法存在BUG:把对象中的某些属性值变为字符串的时候,会存在问题
     + 正则变为 {}
     + 日期对象变为日期字符串
     + 属性值为 function/undefined/Symbol/BigInt 的属性会消失(直接不拷贝)
    所以这种方法适用于数据值为 “number/null/boolean/普通对象/数组对象”等内容的对象/数组
*/

// 方案二:深层遍历克隆每一层(自实现)
 function cloneDeep(obj) {
  // 判断obj的类型:如果不是object类型,且不为null的,则直接返回
  if(typeof obj !=='object') return obj;
  if(obj === null) return null;
 
  // 否则获取其构造函数 constructor
  let constructor = obj.constructor;
  if(constructor.name === 'RegExp') {  // 如果是正则就创建一个正则实例
    return new constructor(obj);
  }
  let clone = new constructor;
  for(let key in obj) {
    if(!obj.hasOwnProperty(key)) break; // 判断是不是私有属性
    // 为了避免对象中的某个属性用的还是这个对象,导致的循环嵌套(死递归)
    if(obj === obj[key]) {
      clone[key] = obj[key];
      break;
    }
    clone[key] = cloneDeep(obj[key]);
  }
  return clone;
}
let newObj = cloneDeep(obj)
console.log(obj, newObj);
console.log(obj.g === newObj.g);  // true  因为没有对函数进行处理(感觉没必要单独克隆函数)
console.log(typeof obj.h, typeof newObj, obj.h === newObj.h); // 'object' 'object' false 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值