深拷贝,事件总线

62 阅读3分钟

深拷贝和事件总线

引用赋值/浅拷贝/深拷贝的区别和关系

console.log(window.window === window)

const info = {
  name: "lzs",
  age: 18,
  friend: {
    name: "kobe"
  },
  running: function() {},
  [Symbol()]: "abc",
  // obj: info
}
info.obj = info

// 1.操作一: 引用赋值
// const obj1 = info

// 2.操作二: 浅拷贝
// const obj2 = { ...info }
// // obj2.name = "james"
// // obj2.friend.name = "james"
// // console.log(info.friend.name)

// const obj3 = Object.assign({}, info)
// // obj3.name = "curry"
// obj3.friend.name = "curry"
// console.log(info.friend.name) curry

注意:

1、Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象,继承属性和不可枚举属性是不能拷贝的。

2、针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。

3、目标对象自身也会改变

4、异常会打断后续拷贝任务

json实现深拷贝

function deepClone(obj) {
    const newObj = JSON.parse(JSON.stringify(info))
    return newObj
}

实现深拷贝

基本实现(递归)

function deepCopy(origin) {
    if(!isObject(origin)) {
        return origin
    }
    const newObj = {}
    for(const key in origin) {
        newObj[key] = deepCopy(origin[key])
    }
    return newObj
}

考虑数组

function deepCopy(originValue) {
      // 1.如果是原始类型, 直接返回
      if (!isObject(originValue)) {
        return originValue
      }

      // 2.如果是对象类型, 才需要创建对象
      const newObj = Array.isArray(originValue) ? []: {}
      for (const key in originValue) {
        newObj[key] = deepCopy(originValue[key]);
      }
      return newObj
    }

考虑其他数据类型

 function deepCopy(originValue) {
      // 0.如果值是Symbol的类型
      if (typeof originValue === "symbol") {
        return Symbol(originValue.description)
      }

      // 1.如果是原始类型, 直接返回
      if (!isObject(originValue)) {
        return originValue
      }

      // 2.如果是set类型
      if (originValue instanceof Set) {
        const newSet = new Set()
        for (const setItem of originValue) {
          newSet.add(deepCopy(setItem))
        }
        return newSet
      }

      // 3.如果是函数function类型, 不需要进行深拷贝
      if (typeof originValue === "function") {
        return originValue
      }

      // 2.如果是对象类型, 才需要创建对象
      const newObj = Array.isArray(originValue) ? []: {}
      // 遍历普通的key
      for (const key in originValue) {
        newObj[key] = deepCopy(originValue[key]);
      }
      // 单独遍历symbol
      const symbolKeys = Object.getOwnPropertySymbols(originValue)
      for (const symbolKey of symbolKeys) {
        newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey])
      }

      return newObj
    }

使用weakmap解决循环引用

  • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key;

  • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;

function deepCopy(obj, map = new WeakMap()) {
    if(typeof obj === "Symbol") {
        return Symbol(obj.description)
    }
    if(!isObject(obj)) {
        return obj
    }
    if(obj instanceof Set) {
        let set = new Set()
        for(let item of obj) {
            set.add(deepCopy(item))
        }
        return set
    }
    if(typeof obj === "function") {
        return obj
    }
    if(map.get(obj)) return map.get(obj)
    const newObj = Array.isArray(obj)?[] : {}
    map.set(obj, newObj)
    
    for(const key in obj){
        newObj[key] = deepCopy(obj[key],map)
    }
    const SymbolKeys = Object.getOwnPropertySymbols(obj)
    for(const key of symbolKeys) {
        newObj[Symbol(key.description)] = deepCopy(obj[key],map)
    }
    return newObj
}

事件总线

  • class LzsEventBus
  • 方法
    • on(eventName, eventFn)
    • emit(eventName, ...args)
    • off(eventName, eventFn)
class LzsEventBus {
    constructor() {
        this.eventMap = {}
    }
    
     on(eventName, eventFn) {
        let eventFns = this.eventMap[eventName]
        if (!eventFns) {
          eventFns = []
          this.eventMap[eventName] = eventFns
        }
        eventFns.push(eventFn)
      }
    
      off(eventName, eventFn) {
        let eventFns = this.eventMap[eventName]
        if (!eventFns) return
        for (let i = 0; i < eventFns.length; i++) {
          const fn = eventFns[i]
          if (fn === eventFn) {
            eventFns.splice(i, 1)
            break
          }
        }
        if(eventFns.length === 0) {
            delete this.eventMap[eventName]
        }
      }
       
      emit(eventName, ...args) {
          let eventFns = this.eventMap[eventName]
          if(!eventFns) return
          
          eventFns.forEach(fn => {
              fn(...args)
          })
      }
      
      once(eventName, eventFn) {
        const execFn = () => {
          eventFn();
          this.off(eventName, execFn);
        };
        this.on(evt, execFn);
      }

}

并发的异步调度器

JS 实现一个带并发限制的异度调度器 Scheduler,保证同时运行的任务最多有两个。完善下面代码中的 Scheduler 类,使得以下程序能正确输出。

class Scheduler {
  add(promiseMaker) {}
}

const timeout = (time) =>
  new Promise((resolve) => {
    setTimeout(resolve, time);
  });

const scheduler = new Scheduler();
const addTask = (time, order) => {
  scheduler.add(() => timeout(time).then(() => console.log(order)));
};

addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
// output:2 3 1 4
// 一开始,1,2两个任务进入队列。
// 500ms 时,2完成,输出2,任务3入队。
// 800ms 时,3完成,输出3,任务4入队。
// 1000ms 时,1完成,输出1。
class Scheduler {
  constructor() {
      this.waitTasks = []
      this.excutingTasks = []
      this.max = 2
  }
   add(promiseMaker) {
       if(this.excutingTasks.length < this.max) {
           this.run(promiseMaker)
       }else{
           this.waitTasks.push(promiseMaker)
       }
  }
    run(promiseMaker) {
        const len = this.excutingTasks.push(promiseMaker)
        const index = len -1 
        promiseMaker().then(() => {
            this.excutingTasks.splice(index,1)
            if(this.waitTasks.length > 0){
                this.run(this.waitTasks.shift())
            }
        })
    }
}