一、序列化实现深拷贝的缺点、手写深拷贝
1、借助序列化实现深拷贝,有哪些缺点?
- ①不能拷贝函数
- ②不能拷贝Symbol()
- ③不能拷贝循环引用的对象(会报错,比如window.window.window.window)
- ④key的值为 undefined不会拷贝(但是感觉问题不大)
const _ = require("lodash");
const { cloneDeep } = require("./copy");
let s1 = Symbol("aaa");
let s2 = Symbol("bbb");
const obj = {
name: "why",
age: 18,
friend: {
name: "kobe",
friend: {
name: "james",
},
},
hobbies: ["run", "swing"],
food: null,
country: undefined,
[s1]: "aaa",
[s2]: s2,
run: function () {
console.log(this.name, "在跑步");
},
};
// obj.obj = obj;
const copyObj = JSON.parse(JSON.stringify(obj));
// const copyObj = _.cloneDeep(obj);
// const copyObj = cloneDeep(obj);
// 测试代码
copyObj.name = "copy" + obj.name;
copyObj.friend.name = "copy" + copyObj.friend.name;
copyObj.hobbies.push("eat");
console.log("====================obj======================");
console.log(obj);
console.log("====================copyObj======================");
console.log(copyObj);
// obj.run();
// copyObj.run();
// console.log(obj[s2] === copyObj[s2]);
====================obj======================
{
name: 'why',
age: 18,
friend: { name: 'kobe', friend: { name: 'james' } },
hobbies: [ 'run', 'swing' ],
food: null,
country: undefined,
run: [Function: run],
[Symbol(aaa)]: 'aaa',
[Symbol(bbb)]: Symbol(bbb)
}
====================copyObj======================
{
name: 'copywhy',
age: 18,
friend: { name: 'copykobe', friend: { name: 'james' } },
hobbies: [ 'run', 'swing', 'eat' ],
food: null
}
2、实现对象的深拷贝要注意哪些问题?
- ①对象类型需要递归拷贝
- ②数组和对象类型需要做区分
- ③函数本就是为了实现代码复用,所以函数不需要拷贝,直接再次引用即可
- ④对于symbol类型,使用
for in
语法无法获取,可以使用 Object.getOwnPropertySymbols(obj)
语法
- ⑤对于
obj.obj.obj === obj
这种循环引用,可以使用WeakMap存一份,然后有值直接进行引用赋值即可。避免死循环
// copy.js
function cloneDeep(obj) {
let map = new WeakMap();
return _cloneDeep(obj, map);
}
function _cloneDeep(obj, map) {
if (
typeof obj !== "object" ||
obj === null ||
typeof obj === "function" ||
typeof obj === "symbol"
) {
return obj;
}
if (map.has(obj)) return map.get(obj);
let copyObj = null;
if (Array.isArray(obj)) {
copyObj = [];
} else {
copyObj = {};
}
map.set(obj, copyObj);
for (const key in obj) {
copyObj[key] = _cloneDeep(obj[key], map);
}
for (const key of Object.getOwnPropertySymbols(obj)) {
copyObj[key] = _cloneDeep(obj[key], map);
}
return copyObj;
}
module.exports = {
cloneDeep,
};
3、手写事件总线,实现 emmit、on、off
三个方法
class EventBus {
constructor() {
this.evnentStore = {};
}
on(eventName, callback) {
let list = this.evnentStore[eventName];
if (!list) {
list = [];
this.evnentStore[eventName] = list;
}
if (list.indexOf(callback) < 0) list.push(callback);
}
off(eventName, callback) {
let list = this.evnentStore[eventName];
if (!list) return;
let index = list.indexOf(callback);
if (index >= 0) list.splice(index, 1);
}
emmit(eventName) {
let list = this.evnentStore[eventName];
if (!list) return;
for (const item of list) {
item();
}
}
}
const eventBus = new EventBus();
// 监听一
const func1 = function () {
console.log("function1 listen to aaa");
};
eventBus.on("aaa", func1);
// 监听二
const func2 = function () {
console.log("function2 listen to aaa");
};
eventBus.on("aaa", func2);
// 监听三
const func3 = function () {
console.log("function3 listen to bbb");
};
eventBus.on("bbb", func3);
// 触发事件
eventBus.emmit("aaa");
// 移除事件
eventBus.off("aaa", func2);
// 触发事件
eventBus.emmit("aaa");