乐趣区

关于javascript:愿不负努力所愿皆所求-shopee入职闲叙

写于结尾

通过前段时间的筹备,笔者最近曾经胜利入职 shopee。所以最近没有更新内容,之后稳定下来之后会持续进行输入

写一写入职之后做的事

入职 shopee 之后,导师有给到一个 entry task,这个工作是实现一个事件机制。实现 addEventListenerremoveEventListenerdispatchEvent 这三个办法。

要求是这个样子的:

  • 兼容 W3C 的事件冒泡和事件捕捉模型(addEventListener 的 useCapture 参数)
  • 每个事件将会有一个优先级(由开发者设置,最高为 0,数字越大则优先级越低,若未设置则默认为 0),若同一时刻有多个事件须要被执行,则依照优先级从高到低执行;若其中多个事件优先级雷同,先被定义的事件先执行
  • 在优先级与事件冒泡 / 捕捉模型抵触时,优先保障事件冒泡 / 捕捉的执行程序
  • 须要将你的代码写成一个 TypeScript 模块,引入形式和 API 请参考文档
  • 若屡次为同一元素绑定同一类型的同一 Listener,该事件在符合条件时只触发一次,事件触发优先级以最终注册的优先级为准。

加分项有个比拟有意思的货色:尽可能保障一帧的工夫(16ms)中所有事件的执行工夫之和不超过 10ms(临时无需思考超过 10ms 的单个事件),须要把在这一帧来不及执行的事件放到下一帧执行(仍旧须要依照优先级来执行)

我的思路

W3C 的事件模型是先捕捉后冒泡

对于 addEventListener:

入参为 dom 节点,监听办法名,回调办法,其余配置能够用 …opt 承受。

实现思路

将注册的事件同一解决,创立 weakmap 对象,数据结构如下:

{
    Dom:{handleName(监听办法名):{
        // 寄存解决捕捉和冒泡的数组
            bubble:[{ // 冒泡数组
                cb:()=>void // 事件触发回调
                range:0 // 此事件优先级
                once:false // 兼容 opt 的 once 参数
            }],
            capture:[] // 捕捉数组,同上}
    }
}

办法外部还须要对原来的 dom 节点进行一次监听,用来在用户手动点击触发时的事件。这时候须要将此事件做一个缓存,以便在 removeEventListener 的时候去勾销监听

对于 removeEventListener:

入参为 dom 节点,监听办法名,回调,是否采纳捕捉模型(可选,默认 false)

实现思路:

首先是健壮性解决,而后对 weakmap 对象中对应节点的对应事件做删除解决。而后把在 addEventListener 的时候增加的监听进行 remove。

对于 dispatchEvent:

这个办法应该算是要害,他的入参是 dom 节点和监听办法名。

实现思路:

  • 首先进行健壮性解决,而后递归的将以后节点,父节点的捕捉和冒泡数组存入数组。
  • 顺次对数组中的回调进行调用。
  • 10ms 的实现,应用的 api 是 requestAnimationFrame,rAF 传入一个回调,回调中能够拿到一个 time 参数,time 参数批示以后被 requestAnimationFrame() 排序的回调函数被触发的工夫。在同一个帧中的多个回调函数,它们每一个都会承受到一个雷同的工夫戳,即便在计算上一个回调函数的工作负载期间曾经耗费了一些工夫。该工夫戳是一个十进制数,单位毫秒,最小精度为 1ms(1000μs)(– 来自 MDN)。再将 performance.now()运行失去的工夫戳和以后的 rAF 回调承受到的 time 进行比照,如果在 10ms 内能够持续从数组中取事件进行调用。反之,则放入下一帧
  • once 的实现,如果有 once 参数,就对以后的对象援用置为空对象

这里能够提供一下我的思路,大家也能够本人尝试写一下。

// 递归的去将以后节点和父节点存入数组
function recurrenceFindNodeList(caps: callbackType[],
  bubs: callbackType[],
  node: nodeType,
  handleName: string
) {
  const parent: any = node.parentNode;
  if (eventMap.has(parent)) {const parentObj = eventMap.get(parent)[handleName];
    caps = [...parentObj.capture, ...caps];
    bubs = [...bubs, ...parentObj.bubble];
    recurrenceFindNodeList(caps, bubs, parent, handleName);
  }
  return [...caps, ...bubs];
}

// 对于 10ms 的实现

requestAnimationFrame(handler);
function handler(time: number) {let taskFinishTime: number = window.performance.now();
    while (taskFinishTime - time < 10) {const nextTask = tasklist.shift();
      if (nextTask?.cb) {nextTask.cb();
      }
      taskFinishTime = window.performance.now();}
    if (tasklist.length > 0) {requestAnimationFrame(handler);
    }
  }

写于最初

shopee 是一个十分年轻化的公司,在这里从技术角度说,能够学习到很多新技术,并加入他们的我的项目从 0 到 1 的过程,置信在这里的提高会很大。如果大家想理解虾皮欢送加我微信:zhi794855679。

愿不负致力,所愿皆所求。

退出移动版