近期的一次面试,面试官出了一个题目:实现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重置。