深拷贝
解构赋值
只针对一层
let a = {
name: '盏灯',
age: 18
}
let b = { ...a }
b.name = '一盏灯'
console.log('a', a)
console.log('b', b)
结果, 确实改变b,a不受影响不会改变,正是我们要的效果。
再来看看这种es6展开语法多层的情况。
let a = {
name: '盏灯',
age: 18,
father: {
father: '111'
}
}
let b = { ...a }
b.name = '一盏灯'
console.log('a', a)
console.log('b', b)
// 改最外面(也就是第一层)没问题,改b不会影响到a
b.father.father = '222'
console.log('a', a)
console.log('b', b)
// 这个时候,当改动b的father的father的时候,a的father的father也改变了。
// 所以这种方法,如果对象只有一层,用这种办法没问题;如果是多层这种方法就🚫
JSON.parse(JSON.stringify(待拷贝对象))
这个一层,多层都可以拷贝,没问题,而且改变b不会影响a的值
可以看到改变b的爷爷,a的爷爷是不受影响的。
但是,
如果对象中有函数,那情况就不同了
let a = {
name: '盏灯',
age: 18,
father: {
father: '111'
},
say () {
console.log('hello world')
}
}
let b = JSON.parse(JSON.stringify(a))
console.log('a', a)
console.log('b', b)
// 会发现b中没有拷贝到a的方法
lodash.cloneDeep
lodash的深拷贝
lodash是一个实用且高性能的js实用工具库。
原生html引用cdn方式
<script type="module">
import lodash from 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/+esm'
console.log(lodash)
console.log(lodash.cloneDeep)
let a = {
name: '盏灯',
age: 18,
father: {
father: '111'
},
say () {
console.log('hello world')
}
}
let b = lodash.cloneDeep(a)
b.name = '一盏灯'
console.log('a', a)
console.log('b', b)
b.father.father = '222'
console.log('a', a)
console.log('b', b)
</script>
通过npm方式
npm i --save lodash
import _ from 'lodash'
let a = {
name: '盏灯',
age: 18,
father: {
father: '111'
},
say () {
console.log('hello world')
}
}
let b = _.cloneDeep(a)
b.father.father = '222'
console.log(a)
console.log(b)
从截图可以看到
用lodash这个库
1、能多层拷贝
2、改变拷贝过来的b,被拷贝的a不会影响。(完全变地址栈)
3、函数也没问题
那我自己封一个可不可以
可以
1、浅的,一层的,这样写
function clone (target) {
let cloneTarget = {}
for (const key in target) {
cloneTarget[key] = otarget[key]
}
return cloneTarget
}
cloneTarget: 拷贝出来的 target: 被拷贝的
2、不行啊,我要深的。深的,这样写。
function clone (target) {
if (typeof target === 'object') {
let cloneTarget = {}
for (const key in target) {
cloneTarget[key] = clone(target[key])
}
return cloneTarget
} else {
return target
}
}
3、
这个深拷贝还不够,需要考虑以下问题,更进一步去探索
-
不可枚举属性 和 Symbol类型 不可复制的问题,用什么来解决。
答:Reflect.ownKeys()Reflect.ownKeys() 方法返回一个由目标对象自身的属性键组成的数组。跟Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) 得到的一样。
-
当参数值为Date、RegExp类型时,直接生成一个新的实例并返回。
-
利用Object.getOwnPropertyDescriptors()方法 获得 对象的所有属性以及对应的特性。
也就是说,这个方法返回给定对象的所有属性的信息,包括有关getter和setter的信息。所有属性都要克隆出来。不能丢。 -
使用Object.create()方法创建一个新的对象,并继承传入原对象的原型链。
Object.create()方法会创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
5、使用WeakMap类型作为Hash表。
WeakMap 是 弱引用类型,可以防止内存泄漏,所以可以用来检测循环引用,如果存在循环,则引用直接返回WeakMap存储的值。
WeakMap 自动垃圾回收,避免内存泄漏
WeakMap的特性就是,保存在其中的对象不会影响垃圾回收,如果WeakMap保存的节点,在其他地方都没有被引用了,那么即使它还在WeakMap中也会被垃圾回收回收掉。
function cloneDeep(entity, cache = new WeakMap()) {
const referenceTypes = ['Array', 'Object', 'Map', 'Set', 'Date'];
const entityType = Object.prototype.toString.call(entity);
if (
!new RegExp(referenceTypes.join('|')).test(entityType) ||
entity instanceof WeakMap ||
entity instanceof WeakSet
)
return entity;
if (cache.has(entity)) {
return cache.get(entity);
}
const c = new entity.constructor();
if (entity instanceof Map) {
entity.forEach((value, key) =>
c.set(cloneDeep(key), cloneDeep(value))
);
}
if (entity instanceof Set) {
entity.forEach((value) => c.add(cloneDeep(value)));
}
if (entity instanceof Date) {
return new Date(entity);
}
cache.set(entity, c);
return Object.assign(
c,
...Object.keys(entity).map((prop) => {
// debugger;
return {
[prop]: cloneDeep(entity[prop], cache),
};
})
);
}
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天 点击查看活动详情