JavaScript中的深拷贝和浅拷贝问题

1.概念

深拷贝实质上是拷贝要拷贝的对象自身,浅拷贝实质上是对要拷贝对象的引用。

  • 当你浅拷贝复制A为B的时候,当数据类型复杂的时候,改动B会给A造成影响;
  • 当你深拷贝复制A为B的时候,任何时候,改动B都不会给A造成影响。如果只改动简单数据类型后,改动B会给A造成影响,那这个操作就不是拷贝。

2.直接用等号赋值不是拷贝

在复杂数据类型中,改动B会给A造成影响:

let obj1 = {
  name: "bob",
};
let obj2 = obj1;
obj2.name = "mike";
obj2.age = 23;
console.log("obj1", obj1);
console.log("obj2", obj2);

在这里插入图片描述

如果是简单的数据类型,相当于之前给之前的赋值替掉了,所以不会造成影响:

let obj1 = "bob";
let obj2 = obj1;
obj2 = "mike";
console.log("obj1", obj1);
console.log("obj2", obj2);

在这里插入图片描述

3.浅拷贝

注意:对于对象的浅拷贝,不能复制原型链上的属性和方法。

3.1 扩展运算符

数组(es6中浅拷贝):

let a = [
  1,
  2,
  3,
  {
    name: "bob",
    age: 12,
  },
];
b = [...a];
b[2] = 100;
b[3].age = 15;
console.log("a", a);
console.log("b", b);

在这里插入图片描述

对于对象,需要该对象拥有iterater接口。这个之间讲过。
在这里插入图片描述

3.2 Object.assign()

可以拷贝对象。

let person1 = {
  name: "bob",
  age: 12,
  arr: [1, 2, 3],
};
let person2 = {};
person2 = Object.assign(person2, person1);
person2.age = 19;
person2.arr[1] = 10000;
console.log("person1", person1);
console.log("person2", person2);

在这里插入图片描述

3.3 concat()

let arr1 = [
  1,
  2,
  3,
  {
    name: "bob",
    age: 18,
  },
];
let arr2 = [];
arr2 = arr2.concat(arr1);
arr2[1] = 1000;
arr2[3].age = 99;
console.log(arr1);
console.log(arr2);

在这里插入图片描述

3.4 Array.prototype.slice()

const arr = [1, 2, { foo: "foo" }];
const _arr = arr.slice();
_arr[1] = 9999;
_arr[2].foo = "bar";
console.log(arr);
console.log(_arr);

在这里插入图片描述

3.5 手动实现浅拷贝

//for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
function shallowClone(obj) {
  if (obj === null || obj === undefined) {
    return obj;
  }
  // 数据是基本型数据,直接返回
  if (typeof obj !== "object") return obj;
  // 对象为空,直接返回
  //Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
  if (!Object.keys(obj).length) return obj;
  // 据obj的类型判断是新建数组还是对象
  let newClone = Array.isArray(obj) ? [] : {};
  //for...in循环:只遍历对象自身的和继承的可枚举的属性。
  for (let key in obj) {
    //判断当前对象/数组是否有自身的属性 不包括继承
    // 核心
    if (obj.hasOwnProperty(key)) {//确保不拷贝原型链上的属性
      // 赋值给新对象/数组
      newClone[key] = obj[key];
    }
  }
  return newClone;
}

测试一下:

let person1 = {
  name: "bob",
  age: 12,
  arr: [1, 2, 3],
};
let person2 = {};
person2 = shallowClone(person1);
person2.age = 19;
person2.arr[1] = 10000;
console.log("person1", person1);
console.log("person2", person2);

const arr = [1, 2, { foo: "foo" }];
const _arr = shallowClone(arr);
_arr[1] = 9999;
_arr[2].foo = "bar";
console.log(arr);
console.log(_arr);

const str1 = "123";
const str2 = shallowClone(str1);
str2 = "111";
console.log("str1 : ", str1);
console.log("str2 : ", str2);

在这里插入图片描述

4.深拷贝

4.1 使用JSON.parse(),JSON.stringify()

这个方法有个问题,就是无法处理函数和正则,函数会丢失,正则会转换为 null 对象。

const obj = {
  name: "bob",
  age: 12,
  arr: [1, 2, 3],
};
const _obj = JSON.parse(JSON.stringify(obj));
_obj.arr[2] = 999;
console.log(obj);
console.log(_obj);

在这里插入图片描述

4.2 lodash库

lodash是一个JS库,我们可以使用lodash中的cloneDeep实现深拷贝。

我这里用node安装了lodash,然后把lodash文件夹中的lodash.min.js拿出来。

npm init -y
npm install lodash
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./lodash.min.js"></script>
  </head>
  <body>
    <script>
      const obj = {
        name: "bob",
        age: 12,
        arr: [1, 2, 3],
      };
       const _obj = _.cloneDeep(obj);
      _obj.arr[2] = 999;
      console.log(obj);
      console.log(_obj);
    </script>
  </body>
</html>

在这里插入图片描述

4.3 递归函数

// 深拷贝:采用循环递归方式
function deepClone(obj, wm = new WeakMap()) {
	// 对数据预处理
	if(obj === undefined) return undefined
	if(obj === null) return null
	if(obj instanceof Date) return new Date(obj)
	if(obj instanceof RegExp) return new RegExp(obj)
	if(obj instanceof Error) return new Error(obj.message)
	
	// 中断条件
	// 若数据是基本类型,则直接返回不拷贝
	// 对象为空,则直接返回不拷贝
	if(typeof obj !== 'object') return obj
	if(!Object.keys(obj).length) return obj
	// 若WeakMap已存在指定键的元素,则直接存储返回元素,不必重新建立弱引用,节约空间
	if(wm.has(obj)) return wm.get(obj)
	let newClone = Array.isArray(obj) ? [] : {}; 
	wm.set(obj, newClone)
	for(let key in obj) {
		if(obj.hasOwnProperty(key)) {
			// 核心
			newClone[key] = deepClone(obj[key], wm)
		}
	}
	return newClone
}

测试一次:

const obj = {
  name: "bob",
  age: 12,
  arr: [1, 2, 3],
};
const _obj = deepClone(obj);
_obj.arr[2] = 999;
console.log(obj);
console.log(_obj);

let str1 = 12;
let str2 = deepClone(str1);
str2 = 99;
console.log(str1);
console.log(str2);

在这里插入图片描述

5.总结

浅拷贝:

  • 扩展运算符
  • Object.assign
  • slice
  • concat
  • 手写

深拷贝:

  • JSON.parse(),JSON.stringify()
  • lodash库
  • 递归实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值