以下为ChatGPT自动生成的解答,正确与否请自行判断。 也许你也可以从大家的发帖或者 官方解答中找到想要的答案!

BFE.devAI解答 - JavaScript 代码题
7. 手写debounce()并支持leading 和 trailing

解题思路:

  • 和debounce类似,我们需要定义一个守卫函数来决定延迟后函数执行的方案。
  • 然后需要根据leadingtrailing来判断如何处理函数的执行。如果leadingtrue,则在起始时间执行函数,在trailingtrue的情况下,函数延迟执行。
  • 判断这些条件并实现它们,我们需要使用条件和三元操作符多次运算。一些标准的算术操作在其中也很重要。

初学者实现代码如下(打开注释):

function debounce(callback, time, options) {  let timeoutId;    return function(...args) {    const leadingResult =       options?.leading && !timeoutId         ? callback.apply(this, args)         : undefined;            clearTimeout(timeoutId);        timeoutId = setTimeout(() => {      const trailingResult =         options?.trailing && !options?.leading           ? callback.apply(this, args)          : undefined;            timeoutId = undefined;            console.log({ leadingResult, trailingResult }); // log result    }, time);        console.log({ leadingResult }); // log result    return leadingResult; // return result  };}

完整代码:

function throttle(callback, time, options) {  let shouldWait = false;  let timerId = undefined;    return function(...args) {    const shouldExecuteLeading = options?.leading && timerId === undefined;    const shouldExecuteTrailing =       !shouldWait && options?.trailing && timerId !== undefined;    const shouldExecute =       shouldExecuteLeading || shouldExecuteTrailing;        if (shouldExecute) {      callback.apply(this, args);            timerId = setTimeout(() => {        timerId = undefined;            }, time);    } else {      clearTimeout(timerId);            timerId = setTimeout(() => {        callback.apply(this, args);                timerId = undefined;      }, time);    }        shouldWait = true;        setTimeout(() => {      shouldWait = false;        }, time);  };}

可以看到我们添加了两个参数:

function throttle(callback, time, options) {  ...}

我们用一个布尔值来确认函数是否可以执行:

let shouldWait = false;

当然这个值最开始是false的。这个值不能只在定时器里使用,而是在下一次瞬间也需重置。

执行函数的前提是满足leadingtrailing两个条件之一。我们需要检查 leading的情况并判断定时器是否存在.

const shouldExecuteLeading = options?.leading && timerId === undefined;

这里它确实需要是等于undefined还是null.

如果它们不同到底表示什么并且为何?如果经过允许它等于null的测试,那么null会被错误地解释为函数未缓存,出现了一个额外的错误。由此得出结论:undefined是唯一的正确选项.

然后,我们应该在没有延迟的情况下允许trailing处理。如果函数已经被delay,就不需要再激活它了。

const shouldExecuteTrailing =   !shouldWait && options?.trailing && timerId !== undefined;

我们现在可以用它定义判断逻辑:

const shouldExecute =   shouldExecuteLeading || shouldExecuteTrailing;if (shouldExecute) {  // execute immediately} else {  // delay}

现在让我们来看看到底是哪里执行的。

Leading activation

leading回调函数的调用:

if (shouldExecuteLeading) {  callback.apply(this, args);    timerId = setTimeout(() => {    timerId = undefined;        }, time);}

这里真的很简单:执行函数,并将计时器建立绑定的函数进行更新。

Trailing activation

callback函数移动到轮廓线下的时间取决于你地等待时间。

if (!shouldWait) {  if (shouldExecuteTrailing) {    clearTimeout(timerId);        callback.apply(this, args);        timerId = setTimeout(() => {      timerId = undefined;          }, time);  } else {    clearTimeout(timerId);        timerId = setTimeout(() => {      callback.apply(this, args);            timerId = undefined;    }, time);  }}

我们从条件中剥离了其他变量,这使得它更具意义。如果我们需要激活trailing状态的回调函数,我们必须从调度器中拿出定时器,触发函数调用,以便后续函数不会再执行。

对于拖尾函数,我们现在有两种方式定义:

  • 如果其满足shouldExecuteTrailing条件,就直接立即激活。
  • 如果这不是leading callback,等待后执行。

无论我们以哪种方式执行函数,最后一步都是将计时器重置为undefined:

timerId = undefined;

请注意,无论leading状态如何,每个计时器都需要执行delay,因为正如我之前所说的,前一个可能被完全延迟,进而时间增加。

如果要重置状态,则需要将一个should等待的布尔值延迟到下一个时间周期:

shouldWait = true;  setTimeout(() => {  shouldWait = false;    }, time);

这样做让我们清楚什么时候再次开始。可能最好不要延迟,因为问题只会变得更多。但是,如果在下一次的“形状线下方”例如需要10毫秒,我们在一次测试中看到只有13个重复。我们从来没有达到过100次。异步代码与异步调度器的所需的封装相同。