js中的浅拷贝与深拷贝

在前端开发中的过程中我们经常要接触到浅拷贝与深拷贝的问题,
下面就对浅拷贝与深拷贝的概念区别以及其有哪种实现方法来做一个简单的说明。

概念

浅拷贝:

浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是原始数据类型,拷贝的就是原始数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

深拷贝:

深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它会在堆内存空间中创建一个新的实体对象,新的实体对象内容与被拷贝对象的实体对象内容相同,然后新拷贝出来的对象在栈内存中的对象指针就会指向这个新的实体对象,因此拷贝前后的两个对象指向的是两个内存地址不同但内容相同的实体对象。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败。
在这里插入图片描述图中obj2是深拷贝obj1得出的对象,两者指向的不是同一个实体对象,obj2指向的是一个在堆内存中新创建的实体对象,并且这个实体对象的内容与原本obj1对象指向的实体对象内容相同。

区别

在拷贝原始数据类型的时候其实是不区分深拷贝与浅拷贝的,因为都是拷贝原始数据类型的值。当拷贝的是引用数据类型的时候,则区分浅拷贝、深拷贝,因为浅拷贝只复制引用数据类型的第一层属性、深拷贝可以对引用数据类型的属性进行递归复制

实现方法

浅拷贝的实现方法:

1、Object.assign()

Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

该方法需要注意的是:

  • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
  • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
  • 因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,会报错。
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);  
console.log(target);  // {a: 1, b: 2, c: 3}

2、扩展运算符

使用扩展运算符可以在对对象或数组的第二层开始的拷贝是浅拷贝。

let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

3、数组方法实现数组浅拷贝

(1)Array.prototype.slice (单层深拷贝,多层浅拷贝)

slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

let arr = [1,2,3,4];
console.log(arr.slice()); // [1,2,3,4]
console.log(arr.slice() === arr); //false

(2)Array.prototype.concat (单层深拷贝,多层浅拷贝)

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。

let arr = [1,2,3,4];
console.log(arr.concat()); // [1,2,3,4]
console.log(arr.concat() === arr); //false

4、实现浅拷贝函数

// 浅拷贝的实现;

function shallowCopy(object) {
  // 只拷贝对象
  if (!object || typeof object !== "object") return;
  // 根据 object 的类型判断是新建一个数组还是对象
  let newObject = Array.isArray(object) ? [] : {};
  // 遍历 object,并且判断是 object 的属性才拷贝
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = object[key];
    }
  }
  return newObject;
}
深拷贝的实现方法:

1、JSON.stringify()

JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。
这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

let obj1 = {  a: 0,b: {c: 0}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}

2、函数库lodash的_.cloneDeep方法

该函数库也有提供_.cloneDeep用来做 Deep Copy

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

3、扩展运算符
使用扩展运算符可以在对对象或数组的第一层的拷贝是深拷贝。

let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

4、实现深拷贝函数(解决循环引用)

// 深拷贝的实现
function deepCopy(obj, map) {
  //判断是否是第一次调用deepCopy方法,是的话创建一个weakmap实例来装遍历过程中出现过的对象
  if(!map){
		map = new WeakMap()
	}
	//判断传入的obj是否为对象
  if( obj === null || typeof obj !== 'Object' ){
    return obj
  }
  //如果map中已经存在这个对象说明出现了循环引用问题
  if(map.get(obj)){
  	return obj
  }
  //map中没有就往map中存入该对象
  map.set(obj,obj)
  //根据obj的类型来给newObj创建初始值
  let newObj = Array.isArray(obj) ? [] : {}
	//遍历obj
  for(let i in obj){
	 if(obj.hasOwnProperty(i)){ //判断当前属性是否为obj自身的属性
	    if(typeof obj[i] === 'Object'){ //判断当前属性是否为对象类型
		    newObj[i] = deepCopy(obj[i],map) //如果是对象类型就使用该方法进行递归处理
	    }else{
			newObj[i] = obj[i] //不是对象类型就直接拷贝
		}
	 }
  }
  return newObj //返回拷贝完成的newObj
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值