JS实现链式调用print().sleep().print()

147 阅读2分钟

近期的一次面试,面试官出了一个题目:实现print函数,该函数可打印传入的参数,并可链式调用函数sleep进行延时打印,即print(10).sleep(1000).print(20)的执行结果为,立即打印10,1s后打印20。

当时现场自己紧张了,没能充分思考便作答,答案必然是没写出来,回家后搜索了相关知识,一般是用class及数组Array存储函数调用来求解,但本着延续自己现场答题的思路求解,便有了如下答案(也是因为觉得网上答案不太满足题目需求,因为那需要new一个实例,再进行调用)。

上代码:

const T = {
  times: [],
};
function print(param) {
  // 重置times,使每个调用链互相独立
  if (this != T) {
    T.times = [];
  }
  // 计算延迟时间,对times求和
  let time = T.times.reduce((p, c) => {
    return p + c;
  }, 0);
  
  if (time) {
    setTimeout(() => {
      console.log(param);
    }, time);
  } else {
    console.log(param);
  }
  
  return T;
}
function sleep(time) {
  // 重置times,使每个调用链互相独立
  if (this != T) {
    T.times = [];
  }
  // 存入延时时间
  T.times.push(time);
  
  return T;
}
T.sleep = sleep;
T.print = print;

函数调用结果为:

print("1.1").sleep(2000).print("1.2").sleep(1000).print("1.3");
print("2.1").print("2.2");
sleep(1000).print("3.1");
//1.1
//2.1
//2.2 立即打印到此
//3.1 1s后打印
//1.2 2s后打印
//1.3 3s后打印

最后,分享解题思路,需要冷静与细致做题,拿到题目后,思考考题关键点,即:链式调用延时执行

观察可知链式调用print("1.1").sleep(2000)是常规函数调用obj.sleep(2000)的一个变形,那么只要函数print返回一个对象obj,且将需重复调用的函数挂载到该对象上即可实现链式调用

js延时操作使用setTimeout,当执行sleep()时,并不能获取到调用链后的print(),故可推测延时操作setTimeout不应在sleep()内进行,而sleep()是影响同一调用链上之后的print(),那么可以将延时参数time存储,让调用链之后的print获取到time后自行处理延时问题。

最后完善细节,考虑每个调用链应互不影响,故times需清零,因print、sleep首次调用时this指向全局,故可通过此差异对times重置。