区别
- 浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的属性值,因此修改拷贝后的属性值是引用类型的,就会影响源对象
- 深拷贝就是对对象以及对象的所有子对象进行拷贝
实现
function cloneDeep(obj) {
if(obj === null) return null;
if (typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 获取obj的构造函数并实例化一个新的
const cloneObj = new obj.constructor();
Object.keys(obj).forEach(key => {
// 递归拷贝属性
cloneObj[key] = cloneDeep(obj[key]);
});
return cloneObj;
}
let obj = {
a: 100,
b: [10, 20, 30],
c: {
x: 10
},
d: new Date(),
f: undefined,
g: /\s+/g,
fn: function () { },
symbol: Symbol.for('Symbol')
};
let arr = [10, [100, 200], {
x: 10,
y: 20
}];
let clone = JSON.parse(JSON.stringify(obj));
console.log(clone);
let clone1 = cloneDeep(obj);
console.log(clone1);
// { a: 100,
// b: [ 10, 20, 30 ],
// c: { x: 10 },
// d: '2020-08-01T14:46:12.461Z',
// g: {} }
// { a: 100,
// b: [ 10, 20, 30 ],
// c: { x: 10 },
// d: 2020-08-01T14:46:12.461Z,
// f: undefined,
// g: /\s+/g,
// fn: [Function: fn],
// symbol: Symbol(Symbol) }
// 获取数据类型
function typeOf(any) {
return Object.prototype.toString.call(any).split(' ')[1].slice(0, -1)
}
function deepClone(any) {
// 避免循环引用
const cache = new Map()
// 根据不同类型的数据进行不同的拷贝方式,cache保存已复制属性引用,防止重复引用
function recursionClone(any) {
if (cache.get(any)) {
return cache.get(any)
}
const type = typeOf(any)
// 基本类型直接返回
if (['String', 'Number', 'Null', 'Boolean', 'Undefined', 'Symbol'].includes(type)) {
return any
}
if (type === 'RegExp') {
return new RegExp(any)
}
if (type === 'Date') {
return new Date(any)
}
if (type === 'Map') {
const m = new Map()
any.forEach((subValue, key) => {
m.set(key, recursionClone(subValue))
})
return m
}
if (type === 'Set') {
const m = new Set()
any.forEach((subValue) => {
m.add(recursionClone(subValue))
})
return m
}
// 函数类型使用 new Function 重新生成新的函数
if (type === 'Function') {
return new Function('return ' + any.toString())()
}
let o
if (type === 'Array') {
o = new Array()
}
if (type === 'Object') {
o = new Object()
}
if (o) {
// 引用类型则对其复制后对象进行缓存
cache.set(any, o)
copyOwnProperties(any, o)
return o
}
return null
}
// 复制from的属性到to上
function copyOwnProperties(from, to) {
const names = Object.getOwnPropertyNames(from)
// 遍历自身的可枚举属性(字符串属性和 Symbol属性)
Reflect.ownKeys(from).forEach(key => {
if (from.propertyIsEnumerable(key)) {
to[key] = recursionClone(from[key])
}
})
return to
}
return recursionClone(any)
}
const s = Symbol(1)
const test = {
date: new Date('2022-08-12'),
reg: /\s/g,
func: function (name) {
console.log(name)
},
obj: {
a: 1,
b: 2,
c: 3
},
arr: [1, 2, 3],
a: 1,
b: null,
c: undefined,
d: '1',
e: true,
map: new Map([
[1, 'aaa'],
[2, 'bbb'],
[3, 'ccc']
]),
set: new Set([1, 2, 3]),
symbol: Symbol('Symbol'),
[s]: 2
}
// 循环引用
test.testObj = test
console.log(deepClone(test))
/**
* 什么是深拷贝,和浅拷贝有什么区别,动手实现一个深拷贝
* js中基础类型的值直接存在栈内存中
* 引用类型(对象/函数)存在堆内存中,栈内存中存储指向该堆的地址
* 深拷贝指在堆内存中开辟新的空间,复制属性,栈内存中指针指向新的堆,拷贝对象与被拷贝对象是相互独立的,
* 修改其中一个的属性值不会影响到另一个
* 浅拷贝指仅仅拷贝地址指向,拷贝对象与被拷贝对象指向相同的堆,修改其中一个的属性时会影响到另一个
*/
function deepClone(obj) {
if (typeof obj !== "object" || obj !== null) {
return obj;
}
if (obj instanceof Array) {
const newArr = [];
obj.forEach((e) => {
newArr.push(deepClone(e));
});
return newArr;
}
const newObj = {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const element = obj[key];
newObj[key] = deepClone(element);
}
}
return newObj;
}
const obj1 = {
a: 1,
b: {
c: 2,
},
d: [{ e: { f: 3 } }],
};
const obj2 = deepClone(obj1);
obj1.a = 100;
obj1.b.c.g = [1, [2]];
delete obj1.d[0].e.f;
console.log(obj1);
console.log(obj2);