Skip to content

Latest commit

 

History

History
350 lines (267 loc) · 7.71 KB

26_深拷贝_事件总线.md

File metadata and controls

350 lines (267 loc) · 7.71 KB

自定义深拷贝函数

前面我们已经学习了对象互相赋值的一些关系,分别包括:

  • 引入的赋值: 指向同一个对象,相互之间会影响;
  • 对象的浅拷贝: 只是浅层的拷贝,内部引入对象时,依然会互相影响;
  • 对象的深拷贝: 两个对象不在有任何关系,不会互相影响

前面我们已经可以通过一种方法来实现深拷贝了: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)