js的数据类型分为:简单类型 和 复杂类型;
简单类型:
string, number, null, undefined, Symbol
复杂数据类型:
function, array, object, Date, Math, 正则
简单数据类型是存储在栈中,复杂数据类型存储在 堆中;
堆的空间比栈的空间大;
简单数据类拷贝是在栈中开辟了一个空间,地址不一样;
复杂的数据类型拷贝的时候是在堆中的,只是在栈中开辟了两个空间(指针)去指向堆,两个的地址是一样的,所以一个改变的时候另外一个也会改变;
假如要是简单数据类型实现一个拷贝复制,那么拷贝后的值并不会随着拷贝的改变,是个深拷贝;
a = 1
b = a
a = 2
console.log(b) // b 值仍然为1
假如有一个复杂类型的一个浅拷贝
a={
v:'123'
}
b = a
a.v = '456'
console.log(b) // b 的值也随着改变了 为456
复杂数据类型实现深拷贝的办法?
1,使用JSON.parse(JSON.stringify(要拷贝的对象))
优点:二级以下也可以实现深拷贝
缺点:无法拷贝function,undefined,正则
2,使用 Object.assign(obj1, obj2)
优点:可以拷贝function,undefined,正则
缺点:二级以下不能实现深拷贝
3,使用递归
写一个公共拷贝的函数方便调用,先判断他的类型,之后进行遍历 for in,拿到所对应的key 的值后进行deepClone1进行深拷贝,在返回就好了;
去遍历一个对象,让他复制到一个新的对象,我们去该变他的key和value,用一个崭新的对象去替代他,这也是可以实现一个深拷贝的
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
4,递归深拷贝的问题:循环引用(循环嵌套)
循环引用问题的产生原因可能是对象之间相互引用,也可能是对象引用了其自身,而造成死循环的原因则是我们在进行深拷贝时并没有将这种引用情况考虑进去,因此解决问题的关键也就是可以将这些引用存储起来并在发现引用时返回被引用过的对象,从而结束递归的调用。
/**
* js深拷贝(包括 循环引用 的情况)
*
* @param {*} originObj
* @param {*} [map=new WeakMap()] 使用hash表记录所有的对象的引用关系,初始化为空
* @returns
*/
function deepClone( originObj, map = new WeakMap() ) {
if(!originObj || typeof originObj !== 'object') return originObj; //空或者非对象则返回本身
//如果这个对象已经被记录则直接返回
if( map.get(originObj) ) {
return map.get(originObj);
}
//这个对象还没有被记录,将其引用记录在map中,进行拷贝
let result = Array.isArray(originObj) ? [] : {}; //拷贝结果
map.set(originObj, result); //记录引用关系
let keys = Object.keys(originObj); //originObj的全部key集合
//拷贝
for(let i =0,len=keys.length; i<len; i++) {
let key = keys[i];
let temp = originObj[key];
result[key] = deepClone(temp, map);
}
return result;
}
安装lodash
$ cnpm i lodash -S
使用lodash 实现一个深拷贝
import _ from 'lodash'
var obj = {id:1,name:{a:'xx'},fn:function(){}};
var obj2 = _.cloneDeep(obj);
obj2.name.a = 'obj2';
console.log(obj,obj2)