以下为ChatGPT自动生成的解答,正确与否请自行判断。

BFE.devAI解答 - JavaScript 代码题
84. 创建一个fake timer (setInterval)

这道题目与 #36 非常相似,我们只需重新实现一个静态化的setInterval,实现的方法也非常类似,下面是具体实现:

class FakeTimer {
  install() {
    // 重写 setInterval 函数
    this.oldSetInterval = window.setInterval;
    window.setInterval = (func, delay) => {
      const startTime = Date.now();
      let intervalId;
      const intervalFunc = () => {
        const timePassed = Date.now() - startTime;
        intervalId = this.oldSetInterval(intervalFunc, delay - timePassed);
        func();
      };
      intervalId = this.oldSetInterval(intervalFunc, delay);
      return intervalId;
    };

    // 重写 clearInterval 函数
    this.oldClearInterval = window.clearInterval;
    window.clearInterval = (intervalId) => {
      this.oldClearInterval(intervalId);
    };

    // 重写 Date.now 函数
    this.oldNow = Date.now;
    let now = 0;
    Date.now = () => now;
  }

  uninstall() {
    window.setInterval = this.oldSetInterval;
    window.clearInterval = this.oldClearInterval;
    Date.now = this.oldNow;
  }

  tick() {
    let intervalIds = [];
    while (intervalIds.length) {
      const id = intervalIds.shift();
      this.oldClearInterval(id);
    }
    let now = 0;
    dateNowFunc = Date.now;
    Date.now = () => now;
    setTimeoutFunc = window.setTimeout;
    window.setTimeout = (func, delay) => {
      const start = now;
      intervalIds.push(
        this.oldSetInterval(() => {
          const timePassed = now - start;
          if (timePassed >= delay) {
            this.oldClearInterval(intervalIds.shift());
            func();
          }
        }, 0)
      );
      return intervalIds[intervalIds.length - 1];
    };

    // 时间不断增加
    function increaseTime(amount) {
      now = now + amount;
      tick();
    }
    function tick() {
      for (let i = 0; i < intervalIds.length; i++) {
        const id = intervalIds[i];
        if (this.timeouts[id]) {
          if (this.timeouts[id].timeoutTime <= now) {
            const timeout = this.timeouts[id];
            timeout.func.call(timeout.context);
            // interval repitition
            if (timeout.interval) {
              timeout.timeoutTime = now + timeout.delay;
            } else {
              delete this.timeouts[id];
            }
          }
        } else {
          this.oldClearTimeout(id);
        }
      }
    }
    setInterval(() => {
      now += 1000;
      tick();
    });
    return increaseTime;
  }
}

来看具体的实现方法:

首先重写setInterval函数,使用一个 intervalFunc 作为间隔运行的函数,该函数会在delay时间后调用 func,同时,在每次运行时计算所用的时间,将延迟时间减已用时间就是下次函数运行的时间,从而实现间隔精度。

 window.setInterval = (func, delay) => {
      const startTime = Date.now();
      let intervalId;
      const intervalFunc = () => {
        const timePassed = Date.now() - startTime;
        intervalId = this.oldSetInterval(intervalFunc, delay - timePassed);
        func();
      };
      intervalId = this.oldSetInterval(intervalFunc, delay);
      return intervalId;
 };

接下来重写clearInterval函数

window.clearInterval = (intervalId) => {
      this.oldClearInterval(intervalId);
};

最后是我们需要实现的时间轴函数,主要分为三个部分:

  1. tick(): 用于对于目前所有的定时器进行检查,是否已经超过设定的时间,从而决定是否执行 callback 函数;
  2. increaseTime(amount): 增加秒数的函数,用于测试时控制时间前进的速度
  3. setInterval(() => { ... }): 用于让时间时间不断增加
let now = 0;
dateNowFunc = Date.now;
Date.now = () => now;
setTimeoutFunc = window.setTimeout;
window.setTimeout = (func, delay) => {
  const start = now;
  intervalIds.push(
    this.oldSetInterval(() => {
      const timePassed = now - start;
      if (timePassed >= delay) {
        this.oldClearInterval(intervalIds.shift());
        func();
      }
    }, 0)
  );
  return intervalIds[intervalIds.length - 1];
};

function increaseTime(amount) {
  now = now + amount;
  tick();
}

function tick() {
  for (let i = 0; i < intervalIds.length; i++) {
    const id = intervalIds[i];
    if (this.timeouts[id]) {
      if (this.timeouts[id].timeoutTime <= now) {
        const timeout = this.timeouts[id];
        timeout.func.call(timeout.context);
        // interval repitition
        if (timeout.interval) {
          timeout.timeoutTime = now + timeout.delay;
        } else {
          delete this.timeouts[id];
        }
      }
    } else {
      this.oldClearTimeout(id);
    }
  }
}
setInterval(() => {
  now += 1000;
  tick();
});
return increaseTime;

最后就可以将三个函数组合起来,从而实现静态化的setIntervalclearInterval

完整代码如下: