一个有意思的手写面试题 | 手写“递增计时器”

582 阅读2分钟

前言

前段时间朋友给我发了一个面试题,是这样的:

写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b,...,a+nb 的时间,然后写一个 myClear,停止上面的 mySetInterVal

细看这个题目,它就是一个无限循环的递增计时器。

题目分析:

  • 每次间隔a+nb时间调用fn,那就要使用setTimeout来做定时
  • 如何a+nb时间执行fn,a和b是传入的确定的值,n是递增的,所以要递归调用定时器

题目解析完成,要做的事情就简单了。

方法不止一种,本文会介绍两个实现方法。

常规方法(ES5 函数)

function mySetInterval(fn,a,b) {
  // let timerId = null
  let tag = {
    timerId: 0
  }
  let n = 0

  function innerFuction() {
    tag.timerId = setTimeout(function(){
      fn()
      n++

      console.log("时间间隔:" + (a+n*b))
      
      innerFuction()
    }, a + n*b)
  }

  innerFuction()

  return tag
}

// 清除定时器
function myClear(timerId) {
  clearTimeout(timerId)
}

/*调用函数-如何使用*/

// 要传入的fn函数
function output() {
  console.log('递增计时器')
}

// 调用mySetInterval函数
const tag = mySetInterval(output,100,200)
console.log(tag)

const dom = document.getElementById("demo");

dom.onclick = function () {
  myClear(tag.timerId);
};

此方法中有1处写法值得注意:

为何使用tag对象来存储timerId,而不直接定义一个timerId?

这是因为我们每次循环调用setTimeout,他都会生成一个新的定时器编号,并且这个编号是数字类型。如果我们直接定义一个timerId,我们在初始化mySetInterval函数时,返回的就是第一次的定时器编号,在后面想要清除时,这个编号其实已经无效了,因为已经生成了新的定时器编号。

20230424173949.jpg

从上图中我们就可以清楚地看到,为何无法停止定时器了。

那为什么使用tag.timerId就可以清除了呢?

这是因为对象是引用类型,初始化mySetInterval函数时,返回的tag是一个对象,它是一个引用类型,其实在内存中存储的只是引用地址。当我们在调用clearTimeout时,传递的是tag.timerId,那么它就需要去tag这个对象中获取timerId,就会拿到最新的值,从而把最新的定时器清除,之后也就不会再调用定时器了。

20230424174242.jpg

ES6的class方法

这个方法写起来就比常规方法要简单多了。

const MySetInterval = class {
  _timer = null;
  _num = 0;
  constructor(fn, a, b) {
    this.fn = fn;
    this.a = a;
    this.b = b;
  }

  start() {
    this._timer = setTimeout(() => {
      this._num++;
      
      this.fn();

      console.log('间隔时间:' + (this.a + this._num * this.b))
      
      this.start();
    }, this.a + this._num * this.b);
    
  }

  myClear() {
    clearTimeout(this._timer);
    this._timer = null;
  }
};

如何调用

function output() {
  console.log("sleep");
}

const sleep = new MySetInterval(output, 100, 200);

sleep.start();

const dom = document.getElementById("demo");

dom.onclick = function () {
  sleep.myClear();
};

总结

这道手写面试题,它其实想考察的就是我们的js基础。如:

  • 引用类型与作用域
  • 递归
  • 定时器