js中的深拷贝浅拷贝

  • 了解深拷贝浅拷贝
  • 代码中的深拷贝浅拷贝
  • 拷贝的实现方法

一、了解深拷贝浅拷贝

1.1 深拷贝(值拷贝)

会创建一个一模一样的对象,新旧对象不共享内存,修改新对象不会影响原对象

1.2 浅拷贝(引用拷贝)

只复制指向某个对象的指针,不复制对象本身,新旧对象还是共享同一块内存。修改新对象影响原对象

1.3 区别

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

二、代码中的深拷贝浅拷贝

js中的数据类型:

  • 基本数据类型:字符串(string)、数值(number)、布尔值(boolean)、undefined、null ;
  • 引用数据类型:对象(Object)、数组(Array)、函数(Function);

基本数据类型进行复制,并重新赋值,并不会影响到原来的变量,这就是深拷贝

let myname = '大华';
let age = myname;
age = 22;
console.log(myname); //大华
console.log(age); //22

myname这个变量会在内存中开辟一个地址,存储一个值为大华age这个变量复制了myname所以他们指向的都是同一个地址,同时也指向同一个值,但对 age重新赋值时,会在内存在开辟一个地址来存储另一个值22

引用数据类型进行复制,并重新赋值,会影响到原来的变量,这就是浅拷贝

let obj1 = {
    myname: "大华",
    age: 12
}
let obj2 = obj1;
obj2.age = 22;

//里面的age都变成22
console.log(obj1); 
console.log(obj2);

对于引用数据类型来说,就有了的说法,obj1会开辟一个内存地址,与值相对应(这就是栈),但这个内存地址的值又会被分配到另一个地址,在另一个地址中又创建了对应的值(也就是堆),obj2在进行复制时,就会通过栈一路找到堆进行复制,一旦修改,那么obj1obj2都会受影响,因为它们指向的都是同一个地址。

三、拷贝的实现方法

实际的项目开发过程中,在多数情况下不希望将对象进行浅拷贝,因为值会相互影响,容易出错,可以把浅拷贝转换成深拷贝。

3.1 深拷贝的实现方法

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
}

3.2 浅拷贝的实现方法

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;
}

更多内容链接☛ 将浅拷贝转换成深拷贝,Vue中的浅拷贝与深拷贝

你可能感兴趣的:(js,jq,javascript,前端,开发语言)