对象的浅拷贝和深拷贝
对象的浅拷贝
浅拷贝的实现方式主要有以下几种:
Object.assign({}, obj)
- 扩展运算符
{...obj}
for...in
遍历每个 key,一一赋值for...of
遍历自身的可枚举属性
1.Object.assign
let obj = {
name: 'Danny',
age: 18
}
let o2 = Object.assign({}, obj)
console.log(o2)
2.扩展运算符
let o3 = {...obj}
console.log(o3)
3.for...in
let o4 = {}
for (let key in obj) {
// 剥离原型链属性
if (obj.hasOwnProperty(key)) {
o4[key] = obj[key]
}
}
console.log(o4)
4.for...of
let o5 = {}
// Object.keys() 返回自身的可枚举属性,不包括继承来的
for (let key of Object.keys(obj)) {
o5[key] = obj[key]
}
console.log(o5)
对象的深拷贝
深拷贝的实现方式主要有以下几种:
JSON.parse(JSON.stringify(obj))
- 循环 + 递归
1.JSON 实现
undefined 和 function 会丢失
let obj1 = {
name: 'Danny',
age: 18,
campus: undefined,
fn: ()=>{}
}
let o1 = JSON.parse(JSON.stringify(obj1))
console.log(o1) // { name: 'Danny', age: 18 }
2.循环 + 递归实现
结合网上的一些博客,实现了一个比较完美且易懂的深拷贝方案:可以拷贝对象、数组、函数、正则、日期类型,同时使用
WeakMap
解决了循环引用的问题
-
循环引用
使用WeakMap
结构存储已经被拷贝IDE对象,每一个拷贝的时候就先向WeakMap
查询该对象是否已经被拷贝,如果已经被拷贝则取出该对象并返回 -
函数对象的拷贝
使用
new Function
可以往函数中动态的传递内容,参考
// 判断是否为对象
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function')
&& obj !== null
}
function deepClone(obj, hash = new WeakMap()) {
// 1.初始化 copy
let copy
let Constructor = obj.constructor
switch (Constructor) {
case RegExp:
copy = new Constructor(obj)
break
case Date:
copy = new Constructor(obj.getTime())
break
case Function:
copy = new Constructor('return ' + obj.toString())()
break
default:
// 对象、数组
if (hash.has(obj)) return hash.get(obj)
copy = new Constructor()
hash.set(obj, copy)
}
// 2.遍历每个 key 赋值,遇到对象递归调用 deepClone 创建一个新的副本
for (let key of Object.keys(obj)) {
if (isObj(obj[key])) {
copy[key] = deepClone(obj[key], hash)
} else {
copy[key] = obj[key]
}
}
// 3.返回结果
return copy
}
测试
const obj2 = {
arr: [1, 2, 3],
obj: { name: 'hehe'},
fn: (x, y) => { console.log(x, y) },
date: new Date(1996, 12),
reg: /正则/ig,
n: null,
u: undefined
}
// 环引用,引用自身
obj2.a = obj2
let copy = deepClone(obj2)
console.log(copy)
// 测试函数拷贝
copy.fn(1, 2) // 可以传参
console.log(copy.fn === obj2.fn) // false