JavaScript 深浅拷贝
- 浅拷贝:拷贝的是对象的指针,修改内容相互影响
- 深拷贝:整个对象拷贝到另一个内存中,修改内容互不影响
浅拷贝
浅拷贝:对原始对象属性值的一份精确拷贝,若属性是基本类型,就拷贝其基本类型的值;若属性是引用类型,则拷贝的是内存地址。所以,若其中一个地址变了后就会影响到另一个对象
实现浅拷贝的方式:
- Object.assign()
- ...扩展运算符
深拷贝
深拷贝:对原始对象属性值从内存中完整的拷贝一份,然后在内存中开辟一个新的存储空间存放拷贝对象。所以,若其中一个对象变了不会影响另一个对象
乞丐版
JSON.parse(JSON.stringify())
这种方式最简单,但有以下弊端:
- 当被拷贝对象中有
Date
对象,则拷贝后时间将以字符串
的形式 - 当被拷贝对象中有
RegExp
、Error
对象,则拷贝的结果将得到一个空对象 - 当被拷贝对象中有
undefined
和函数的时,则拷贝的结果将会把函数或undefined
丢失 通过以上的缺陷,我们可以得出该方法是一个不合格的深拷贝方法
简单版本
注意:
- 若是原始类型,无需继续拷贝,直接返回
- 若是引用类型,则需递归遍历
function clone(target) {
if (target === 'object') {
let cloneTarget = {}
for (let key in target) {
cloneTarget[key] = clone(target[key])
}
return cloneTarget
} else {
return target
}
}
初版主要运用了递归思想,但没有考虑数组的情况
数组情况
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {} // 考虑数组
for (let key in target) {
cloneTarget[key] = clone(target[key])
}
return cloneTarget
} else {
return target
}
}
再升级
考虑循环引用,导致内存溢出
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {}
if (map.get(target)) {
// 检查map中有无克隆过的对象, 有则直接返回
return map.get(target)
}
// 没有 - 将当前对象作为key,克隆对象作为value进行存储
map.set(target, cloneTarget)
for (let key in target) {
cloneTarget[key] = clone(target[key], map)
}
return cloneTarget
} else {
return target
}
}
再升级
使用 weakMap 代替 Map。因为弱引用类型会自动被垃圾回收掉重点
再升级
判断引用类型,要考虑
null
和function
的特殊情况
function isObject(target) {
const type = typeof target
return target !== null && (type === 'object' || type === 'function')
}
function clone(target) {
if (!isObject(target)) {
// 不是对象
return target
} else {
// 是对象
}
}
// 获取数据类型
function getType(target) {
return Object.prototype.toString.call(target)
}