ES6深拷贝函数封装
前置:WeakMap数据类型
Map与WeakMap区别:
- Map的键名可以是任意类型({}、[])
- WeakMap的键名只能是对象
例:从垃圾回收来感受WeakMap
const oBtn1 = document.querySelector('#btn1')
const oBtn2 = document.querySelector('#btn1')
oBtn1.addEventListener('click', handleClick1, false)
oBtn2.addEventListener('click', handleClick2, false)
function handleClick1() {}
function handleClick2() {}
oBtn1.remove() // 删除节点,但是不能回收事件回调函数
oBtn2.remove()
// 必须使用这种方法删除回调
handleClick1 = null;
handleClick2 = null
使用WeakMap:
const oBtn1 = document.querySelector('#btn1')
const oBtn2 = document.querySelector('#btn1')
const oBtnMap = new WeakMap()
// 将节点与回调绑定在一起,垃圾回收时一起被回收(键名键值一起回收)
// 键名是弱引用
oBtnMap.set(oBtn1, handleClick1)
oBtnMap.set(oBtn2, handleClick2)
oBtn1.addEventListener('click', handleClick1, false)
oBtn2.addEventListener('click', handleClick2, false)
function handleClick1() {}
function handleClick2() {}
// oBtn1.remove() // 删除节点,但是不能回收事件回调函数
//oBtn2.remove()
换一个角度看,WeakMap的键名必须是对象,弱引用才有意义。
深拷贝函数封装
拷贝之前,需要先判断原始值(origin
)的数据类型再进行下一步操作。
function当做静态数据类型,不做特殊处理。
1)关于null和undefined的判断:
// null undefined
var a = undefined;
console.log(a == null); // true
console.log(a === null); // false
使用 origin === undefined
表达式就能判断origin
是否为null和undefined值。
2)判断Date、RegExp类型数据:
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
3)判断 {} 或 [] :
使用构造器
const obj = {}
console.log(obj)
打印结果:
可以看到,字面量obj是通过其原型上的constructor使用new Object构造出来的,那么是否可以自己手动构造一个obj呢?
const obj = {}
const newObj = new obj.constructor()
console.log(obj) // {}
newObj.a = 1
console.log(newObj); // {a:1}
同理,数组:
const arr = []
const newArr = new arr.constructor()
console.log(arr);
newArr.push(1)
console.log(newArr);
知道这点,直接使用 const target = new origin.constructor()
生成一个新 {} 或 [] 即可,不用再判断了。
封装函数1
function deepClone(origin) {
if (origin === undefined || typeof origin !== 'object') { // 排除null、undefined、非object值
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
// [] -> [], {} -> {}, 不需要判断是对象还是数组
const target = new origin.constructor()
for (let k in origin) {
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k])
}
}
return target;
}
测试1
var obj = {
name: 'yesmore',
age: 18,
info: {
hobby: ['travel', 'piano', {
a: 1
}],
career: {
teacher: 4,
engineer: 9
}
}
};
const newObj = deepClone(obj)
newObj.info.hobby[2].a = 1234
console.log(newObj);
结果:
测试2
let test1 = {}
let test2 = {}
test2.test1 = test1
test1.test2 = test2
console.log(deepClone(test2));
报错:
Uncaught RangeError: Maximum call stack size exceeded
原因:test1和test2分别作为属性值赋值给对方,在拷贝时,会发生重复拷贝,简而言之,就是缺少记录是否拷贝。
解决:使用 hashMap 哈希表方式记录键名是否存在,通过 WeakMap 实现。
封装函数2
function deepClone(origin, hashMap = new WeakMap()) {
if (origin === undefined || typeof origin !== 'object') { // 排除null、undefined、非object值
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
const hashKey = hashMap.get(origin); // 寻找键是否存在
if (hashKey) {
return hashKey
}
// [] -> [], {} -> {}, 不需要判断是对象还是数组
const target = new origin.constructor()
hashMap.set(origin, target)
for (let k in origin) {
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k], hashMap)
}
}
return target;
}
测试3
let test1 = {}
let test2 = {}
test2.test1 = test1
test1.test2 = test2
console.log(deepClone(test2));
结果:
同系列文章请查看:【Github】pre-interview
转载自:https://juejin.cn/post/7049310166939861028