點燈坊

失くすものさえない今が強くなるチャンスよ

如何使 Function 只執行一次 ?

Sam Xiao's Avatar 2019-10-09

實務上某些 Function 會傳入 Array.prototype.forEach(),但我們只想執行一次而已,如符合條件就只執行一次,其餘僅管符合條件,但都不執行,我們該怎麼做呢 ?

Version

macOS Catalina 10.15
VS Code 1.38.1
Quokka 1.0.254
ECMAScript 5
ECMAScript 2015

Array.prototype.forEach()

[1, 2, 3].forEach(x => console.log(x));

很明顯會印出 123

但我們想只印出 1 即可,23 都不要印。

Closure

let fn = () => {
  let count = 0;

  return x => {
    if (count++) return;

    console.log(x);
  };
};

[1, 2, 3].forEach(fn());

一個很直覺的思考方式,希望有個 count variable 幫我們統計,若 count0 就印,其他都不要印。

ECMAScript 要達成這種 private variable,必須依賴 closure,所以特別另外建立 fn() t產生 callback,使 count 成為其 local variable,然後 return 新 function,使其與 count 做連結。

count0 時為 falsy value,因此印出 1,但 count1 之後就為 truth value,就不再繼續執行。

once000

let fn = () => {
  let once = false;

  return x => {
    if (once) return;

    console.log(x);
    once = true;
  };
};

[1, 2, 3].forEach(fn());

一樣使用 closure,但既然只執行一次,其實不必使用 counter,只需一個 boolean flag 即可。

一開始令 oncefalse,因此會印出 1,印完馬上令 oncetrue,從此就不會再印出任何值。

once001

Dynamic Property

let fn = x => {
  fn.once || console.log(x);
  fn.once = true;
};

[1, 2, 3].forEach(fn);

ECMAScript 是動態語言,可以動態建立 property,利用這個語言特性,其實可把動態建立 property 當成 private variable 使用。

一開始沒有 once property,為 Falsy Value,因此 || 會印出 1

然後馬上建立 once property,並令其為 false,之後就再也不會印出任何值。

once002

Conclusion

  • Closure 是 ECMAScript 很精彩的語言特性,在 OOP 必須借用 private field 實現的功能,在 FP 可藉由 closure 實現
  • 動態建立 property 也是 ECMAScript 很精彩的語言特性,利用其不存在 property 為 undefined 的特性,視其為 falsy value,執行完後再動態建立 property 為 ture,當成 local variable 使用