前面我们已经学习了对象互相赋值的一些关系,分别包括:
- 引入的赋值: 指向同一个对象,相互之间会影响;
- 对象的浅拷贝: 只是浅层的拷贝,内部引入对象时,依然会互相影响;
- 对象的深拷贝: 两个对象不在有任何关系,不会互相影响
前面我们已经可以通过一种方法来实现深拷贝了:JSON.parse
- 这种深拷贝的方式其实对于函数、Symbol等是无法处理的;
- 并且如果存在对象的循环引用,也会报错的;
自定义深拷贝函数:
1.自定义深拷贝的基本功能;
function isObject(value) {
const valueType = typeof value
return (value !== null) && (valueType === "object" || valueType === "function")
}
function deepClone(originValue) {
// 判断传入的originValue是否是一个对象类型
if (!isObject(originValue)) {
return originValue
}
const newObject = {}
for (const key in originValue) {
newObject[key] = deepClone(originValue[key])
}
return newObject
}
// 测试代码
const obj = {
name: "why",
age: 18,
friend: {
name: "james",
address: {
city: "广州"
}
}
}
const newObj = deepClone(obj)
console.log(newObj === obj)
obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
2.对Symbol的key进行处理;
3.其他数据类型的值进程处理:数组、函数、Symbol、Set、Map;
function isObject(value) {
const valueType = typeof value
return (value !== null) && (valueType === "object" || valueType === "function")
}
function deepClone(originValue) {
// 判断是否是一个Set类型, 这里是浅拷贝,如果要深拷贝,是需要遍历的
if (originValue instanceof Set) {
return new Set([...originValue])
}
// 判断是否是一个Map类型
if (originValue instanceof Map) {
return new Map([...originValue])
}
// 判断如果是Symbol的value, 那么创建一个新的Symbol
if (typeof originValue === "symbol") {
return Symbol(originValue.description)
}
// 判断如果是函数类型, 那么直接使用同一个函数
if (typeof originValue === "function") {
return originValue
}
// 判断传入的originValue是否是一个对象类型
if (!isObject(originValue)) {
return originValue
}
// 判断传入的对象是数组, 还是对象
const newObject = Array.isArray(originValue) ? []: {}
for (const key in originValue) {
newObject[key] = deepClone(originValue[key])
}
// 对Symbol的key进行特殊的处理
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const sKey of symbolKeys) {
// const newSKey = Symbol(sKey.description)
newObject[sKey] = deepClone(originValue[sKey])
}
return newObject
}
// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")
const obj = {
name: "why",
age: 18,
friend: {
name: "james",
address: {
city: "广州"
}
},
// 数组类型
hobbies: ["abc", "cba", "nba"],
// 函数类型
foo: function(m, n) {
console.log("foo function")
console.log("100代码逻辑")
return 123
},
// Symbol作为key和value
[s1]: "abc",
s2: s2,
// Set/Map
set: new Set(["aaa", "bbb", "ccc"]),
map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}
const newObj = deepClone(obj)
console.log(newObj === obj)
obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2)
4.对循环引用的处理;
function isObject(value) {
const valueType = typeof value
return (value !== null) && (valueType === "object" || valueType === "function")
}
// WeakMap是一个弱引用
function deepClone(originValue, map = new WeakMap()) {
// 判断是否是一个Set类型
if (originValue instanceof Set) {
return new Set([...originValue])
}
// 判断是否是一个Map类型
if (originValue instanceof Map) {
return new Map([...originValue])
}
// 判断如果是Symbol的value, 那么创建一个新的Symbol
if (typeof originValue === "symbol") {
return Symbol(originValue.description)
}
// 判断如果是函数类型, 那么直接使用同一个函数
if (typeof originValue === "function") {
return originValue
}
// 判断传入的originValue是否是一个对象类型
if (!isObject(originValue)) {
return originValue
}
if (map.has(originValue)) {
return map.get(originValue)
}
// 判断传入的对象是数组, 还是对象
const newObject = Array.isArray(originValue) ? []: {}
map.set(originValue, newObject)
for (const key in originValue) {
newObject[key] = deepClone(originValue[key], map)
}
// 对Symbol的key进行特殊的处理
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const sKey of symbolKeys) {
// const newSKey = Symbol(sKey.description)
newObject[sKey] = deepClone(originValue[sKey], map)
}
return newObject
}
// deepClone({name: "why"})
// 测试代码
let s1 = Symbol("aaa")
let s2 = Symbol("bbb")
const obj = {
name: "why",
age: 18,
friend: {
name: "james",
address: {
city: "广州"
}
},
// 数组类型
hobbies: ["abc", "cba", "nba"],
// 函数类型
foo: function(m, n) {
console.log("foo function")
console.log("100代码逻辑")
return 123
},
// Symbol作为key和value
[s1]: "abc",
s2: s2,
// Set/Map
set: new Set(["aaa", "bbb", "ccc"]),
map: new Map([["aaa", "abc"], ["bbb", "cba"]])
}
obj.info = obj
const newObj = deepClone(obj)
console.log(newObj === obj)
obj.friend.name = "kobe"
obj.friend.address.city = "成都"
console.log(newObj)
console.log(newObj.s2 === obj.s2)
console.log(newObj.info.info.info)
自定义事件总线属于一种观察者模式,其中包括三个角色:
- 发布者(Publisher):发出事件(Event);
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler);
- 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的;
当然我们可以选择一些第三方的库:
- Vue2默认是带有事件总线的功能;
- Vue3中推荐一些第三方库,比如mitt;
当然我们也可以实现自己的事件总线:
- 事件的监听方法on;
- 事件的发射方法emit;
- 事件的取消监听off;
class HYEventBus {
constructor() {
// 保存多个事件
this.eventBus = {}
}
on(eventName, eventCallback, thisArg) {
let handlers = this.eventBus[eventName]
if (!handlers) {
handlers = []
this.eventBus[eventName] = handlers
}
handlers.push({
eventCallback,
thisArg
})
}
off(eventName, eventCallback) {
const handlers = this.eventBus[eventName]
if (!handlers) return
const newHandlers = [...handlers]
for (let i = 0; i < newHandlers.length; i++) {
const handler = newHandlers[i]
if (handler.eventCallback === eventCallback) {
const index = handlers.indexOf(handler)
handlers.splice(index, 1)
}
}
}
emit(eventName, ...payload) {
const handlers = this.eventBus[eventName]
if (!handlers) return
handlers.forEach(handler => {
handler.eventCallback.apply(handler.thisArg, payload)
})
}
}
// 创建事件总线
const eventBus = new HYEventBus()
// main.js
// 监听事件总线,这里最好不要写箭头函数
eventBus.on("abc", function() {
console.log("监听abc1", this)
}, {name: "why"})
const handleCallback = function() {
console.log("监听abc2", this)
}
eventBus.on("abc", handleCallback, {name: "why"})
// utils.js
// 发射事件
eventBus.emit("abc", 123)
// 移除监听
eventBus.off("abc", handleCallback)
eventBus.emit("abc", 123)