JS深入理解—对象深拷贝和浅拷贝,手写DeepClone

JS对象之深拷贝和浅拷贝

关于对象的深拷贝和浅拷贝,通俗点讲,对象B复制对象A,当改变B的时候,A跟着变,那么就是浅拷贝,如果A不变,两者相互不影响,那么就是深拷贝。要理解对象的深拷贝和浅拷贝,得先理解 js 中值类型和引用类型在内存中的存储方式,可以看看这篇文章。

数据类型

值类型(Stack):

包括 Number, String, Boolean, Null, Undefined, Symbol(ES6),拷贝操作均属于深拷贝,因为它们操作的是实际值。

引用类型(Heap):

包括 Array, Object, Function 等对象,拷贝操作可以是浅拷贝,也可以是深拷贝。接下来通过示例来揭开深拷贝和浅拷贝的神秘面纱。

浅拷贝

值类型拷贝都是深拷贝(number, string, boolean, null, undefined, symbol)。

let a = "hello";
let b = a;
b = "world";
console.log(a);

// hello

引用类型 array 浅拷贝

let arrA = [1, 2, 3, 4, 5];
let arrB = arrA;
arrB.push(6);
console.log(arrA);

// [1, 2, 3, 4, 5, 6]  A 同时发生了变化

引用类型 object 浅拷贝

let objA = { name: "zhangsan", age: 30, course: { chinese: 100 } };
let objB = objA;
objB.name = "lisi";
objB.course.chinese = 89;
console.log(JSON.stringify(objA));

// {"name":"lisi","age":30,"course":{"chinese":89}}

深拷贝

单个层级

对于单个层级的对象,使用 ES6 语法块 { ... x} 或者 Object.assign() 即可实现深拷贝。对于嵌套对象,使用这两个方法就“歇菜了”。

let objA = { name: "zhangsan", age: 30 };
let objB = {...objA};
// let objB = Object.assign({},objA);
objB.name = "lisi";
console.log(objA);

// {name: "zhangsan", age: 30} 

浅拷贝例子

let objA = { name: "zhangsan", age: 30, course: { chinese: 100 } };
let objB = {...objA};  // 不好用了吧
objB.name = "lisi";
objB.course.chinese = 89;

// {"name":"zhangsan","age":30,"course":{"chinese":89}}

嵌套对象

嵌套对象,比较“暴力”的深拷贝方法是使用 JSON.parse(JSON.stringify(objA))

let objA = { name: "zhangsan", age: 30, course: { chinese: 100 } };
let objB = JSON.parse(JSON.stringify(objA));

objB.name = "lisi";
objB.course.chinese = 89;

// {"name":"zhangsan","age":30,"course":{"chinese":100}}

然而这种方式显得不够优雅,我们来尝试手写一个 工具方法 DeepClone 实现深拷贝吧。

手写DeepClone

直接上代码,利用递归的方式实现对象的层层遍历。下面的代码并不完善,例如没有考虑对 date,file,map, set等对象的拷贝。经典库 loadash 提供了 cloneDeep 来实现深拷贝,阅读其源码,发现对10多种常见的对象做了处理,已经较为完善。

function deepClone(targetObj) {
	let type = Object.prototype.toString.call(targetObj);
	let newObj;
	if (type === "[object Object]") {
	  newObj = {};
	} else if (type === "[object Array]") {
	  newObj = [];
	} else {
	  return targetObj;
	}
	for(key in targetObj) {
	    let value = targetObj[key];
	    newObj[key] = deepClone(value);
	}
	return newObj;
}

你可能感兴趣的:(JS深入理解)