この解答例はChatGPTによって生成されています,正しいかは自己判断で。 皆の投稿または 公式回答ページからも解答例が見つかるかもしれませんよ。

BFE.devでのAI解答例 - JavaScript コーディング問題
7. leading & trailingをサポートするdebounce()を実装する

function debounce(func, wait, option = { leading: false, trailing: true }) {  let timeoutId;  let lastArgs;  let lastThis;  let maxWait;  let result;  let lastCallTime = 0;  let lastInvokeTime = 0;  const leadingCall = () => {    lastInvokeTime = Date.now();    timeoutId = setTimeout(timerExpired, wait);    return option.leading ? invokeFunc(lastArgs, lastThis) : result;  };  const timerExpired = () => {    const timeSinceLastCall = Date.now() - lastCallTime;    const timeSinceLastInvoke = Date.now() - lastInvokeTime;    if (timeSinceLastCall < wait && timeSinceLastCall >= 0) {      timeoutId = setTimeout(timerExpired, wait - timeSinceLastCall);    } else {      timeoutId = null;      if (!option.leading) {        result = invokeFunc(lastArgs, lastThis);      }      if (!timeoutId && maxWait !== wait) {        result = invokeFunc(lastArgs, lastThis);      }    }  };  const invokeFunc = (args, context) => {    lastInvokeTime = Date.now();    lastThis = context;    lastArgs = args;    lastCallTime = lastInvokeTime;    return func.apply(context, args);  };  const leadingTimerExpired = () => {    timeoutId = null;    result = invokeFunc(lastArgs, lastThis);  };  const debounced = (...args) => {    lastArgs = args;    const time = Date.now();    lastThis = this;    if (!lastInvokeTime && option.leading === false) {      lastInvokeTime = time;    }    const timeSinceLastCall = time - lastCallTime;    if (timeSinceLastCall >= wait || timeSinceLastCall < 0) {      if (timeoutId) {        clearTimeout(timeoutId);        timeoutId = null;      }      lastCallTime = time;      result = invokeFunc(args, this);    } else if (!timeoutId && option.trailing !== false) {      timeoutId = setTimeout(        option.leading === false ? leadingTimerExpired : timerExpired,        wait - timeSinceLastCall      );    }    if (option.maxWait !== undefined) {      maxWait = Number(option.maxWait);      const timeSinceLastInvoke = time - lastInvokeTime;      if (timeSinceLastInvoke >= maxWait || timeSinceLastInvoke < 0) {        invokeFunc(args, this);      } else if (!timeoutId) {        timeoutId = setTimeout(timerExpired, maxWait - timeSinceLastInvoke);      }    }    return result;  };  debounced.cancel = () => {    clearTimeout(timeoutId);    lastInvokeTime = 0;    lastArgs = null;    lastThis = null;    lastCallTime = 0;    timeoutId = null;  };  debounced.flush = () => {    if (timeoutId) {      clearTimeout(timeoutId);      timerExpired();    }  };  return debounced;}

解説:

lodash.debounce()ではない手作りのdebounce()を実装する続きです。新しい変数と分岐がたくさんあるので、注意深く読んで実装を見てください。

Code Breakdown:

  1. 関数debounce()を宣言し、以下の引数を受け取ります:
    1. func (Function): 処理したい関数。
    2. wait (Number): 待機する時間(ミリ秒)。
    3. option.leading (Boolean): funcの1回目を呼び出すかどうか。デフォルトは false
    4. option.trailing (Boolean): 待機タイマーの後にfuncを呼び出すかどうか。デフォルトは true
  2. timeoutId変数を宣言し、nullに設定します。
  3. lastArgs変数を宣言し、空配列に設定します。これは、実行される関数に渡される引数を格納します。
  4. lastThis変数を宣言し、nullに設定します。これは、実行される関数のコンテキストを格納します。
  5. maxWait変数を宣言し、nullに設定します。
  6. result変数を宣言し、undefinedに設定します。
  7. lastCallTime変数を宣言し、0に設定します。
  8. lastInvokeTime変数を宣言し、0に設定します。
  9. leadingCall()関数を宣言し、以下の手順で行います。
    1. Date.now()を呼び出し、現在時刻を取得します。これはlastInvokeTimeに保存されます。
    2. setTimeout() を使用して、 waitミリ秒後に timerExpired()関数を呼び出します。これは timeoutId に保存されます。
    3. もし option.leadingtrue なら、invokeFunc() を呼び出して、 result を返します。 そうでなければ resultundefinedのままです。
  10. timerExpired()関数を宣言します。
    1. Date.now()を呼び出して、lastCallTimeから現在時刻までの経過時間を取得します。
    2. Date.now()を呼び出して、lastInvokeTimeから現在時刻までの経過時間を取得します。
    3. もし wait - timeSinceLastCallが0以上である場合で、timeoutIdがnullである場合は、 setTimeout()を使って wait - timeSinceLastCall時間後に timerExpired()関数を呼び出します。
    4. timeoutIdがnullである場合、 option.leadingfalseではない場合は、 resultinvokeFunc()から呼び出します。
    5. !timeoutIdかつmaxWaitwaitと異なる場合、 resultinvokeFunc(lastArgs, lastThis)から呼び出します。
  11. invokeFunc(args, context)関数を宣言します。
    1. Date.now()を呼び出して、現在時刻を取得します。これは、lastInvokeTimeに保存されます。
    2. lastThiscontextをセットします。
    3. lastArgsargsをセットします。
    4. lastCallTimelastInvokeTimeに設定し、それによってこれらの変数が一致するようにします。
    5. contextと同じthisfuncを実行して、その結果を返します。
  12. leadingTimerExpired()関数を宣言し、 resultinvokeFunc()から呼び出し、 timeoutIdをnullに設定します。
  13. lastCallTimelastArgslastThislastInvokeTimetimeoutIdの値を最初の状態にリセットするcancel()関数を宣言します。
  14. timeoutIdがある場合、 timerExpired()関数を直ちに呼び出して、 resultを返すflush()関数を宣言します。
  15. debounced関数を宣言し、以下の手順で行います。
    1. argslastArgsに設定しましょう。
    2. Date.now()を呼び出して現在時刻を取得します。これは time に保存されます。
    3. thislastThisに設定します。
    4. option.leadingfalselastInvokeTimeが nullである場合は、 lastInvokeTimetimeを保存します。
    5. timeSinceLastCallを計算し、 waitよりも大きいまたは0未満である場合は、以下の順番で実行します。
      1. timeoutIdがある場合(前回のタイムアウトがまだ働いている場合)、clearTimeout()関数を呼び出してタイムアウトをキャンセルし、 timeoutIdをnullに設定します。
      2. lastCallTimetimeを設定します。
      3. resultinvokeFunc(args, this)を設定します。
    6. timeoutIdがnullでかつoption.trailingfalseではない場合、以下の手順で実行してください。
      1. setTimeout()を使用して、 wait - timeSinceLastCall時間後にleadingTimerExpired()timerExpired()を呼び出します。前者はoption.leadingfalseのときで、後者はoption.leadingfalseでない場合です。それらのうち、最初の一回だけが実行されます。
      2. timeoutIdを設定します。
    7. option.maxWaitが存在する場合は、以下の手順で実行してください。
      1. maxWaitoption.maxWaitNumber()関数を使って変換します。
      2. timeSinceLastInvokeを計算し、 maxWait以上であるか、または0未満である場合、 invokeFunc(args, this)を呼び出しします。
      3. timeoutIdがnullである場合は、以下の手順で実行してください。
        1. setTimeout()を使用して、 maxWait -timeSinceLastInvoke時間後に timerExpired()を呼び出します。
        2. timeoutIdを設定します。
    8. resultを返します。
  16. cancel()flush()メソッドを、それぞれキャンセルと即座に呼び出すように debounced関数に追加します。
  17. debounced関数を返します。