监听函数的运行轨迹:javaScript进阶-每日一题

163 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

题目 实现spyOn 题目描述 如果你写过单元测试的话,一定很熟悉Spy的用法。

请自己实现一个spyOn(object, methodName)  ,类似于 jest.spyOn()

以下是spyOn需要完成的内容。

  1. spy被调用的时候,原来的method也需要被调用。
  2. spy需要又一个calls数组,数组中含有所有调用的参数

以下代码说明了一切。

const obj = {
   data: 1, 
   increment(num) {
      this.data += num
   }
}

const spy = spyOn(obj, 'increment')

obj.increment(1)

console.log(obj.data) // 2

obj.increment(2)

console.log(obj.data) // 4

console.log(spy.calls)
// [ [1], [2] ]

思路 题目要求监听给定目标的某一函数的调用,并且将参数收集起来,看到该需求的时候脑子里第一时刻想的就是vue2使用的Object.defineProperty(),也就是setter,getter,但是getter好像并不能收集参数,于是更进一步,vue3使用的proxy

我的实现 核心思想就是通过proxy来监听指定函数,通过handler.apply()来监听函数调用。

function spyOn(obj, methodName) {
  const calls = [];
  const proxyedFn = new Proxy(obj[methodName], {
    apply: (target, thisArg, args) => {
      //target:调用函数
      //thisArg: 被调用时的上下文对象。即this指向
      //args: 被调用时的参数数组
      calls.push(args);
      return target.apply(thisArg, args);
    }
  })
  //记得覆盖原始方法
  obj[methodName] = proxyedFn;
  return { calls };
}

其它实现 看完评论区的答案,感觉我好像想的复杂了,直接覆盖就行了...

function spyOn(obj, methodName) {
  if(!obj[methodName] || typeof obj[methodName] != 'function') throw Error('invalid obj.methodName')
  const calls = [];
  const originFn = obj[methodName]
  obj[methodName] = (...args) => {
    calls.push(args);
    return originFn.apply(this, args);
  }
  return { calls }
}