如何寫高品質 function (輸出+輸入篇)

  1. Pure function
  2. 要不要用 return
  3. 兩個輸出要怎辦?

良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。

– 台南原地方法院,最棒的古蹟修復案例之一。基本的從外觀的牆面油漆去除,到馬薩式屋頂的木構造修復。建築最美的雙重圓頂與大廳裝飾,都原汁原味的重現在現在的古蹟內。不只是外觀修復得很美,連內部的構造都依原本的構想,聽說是以零件抽離一件修復一件的方式修復,以確保正確性。也是台南唯一開放貓道,可以參觀馬薩式屋頂木構造的古蹟,長達八年的修復已完成,現在是台南司法博物館。

在說了什麼多的「不要這麼做」之後,偶爾也來寫一下「那要怎麼做」比較好。
這次花一點篇幅來介紹 function 怎麼寫,比較好。(好像語法工匠)
在《忍者》[1]第一版中,直接就指出 JavaScript 的 function 有 4 種

  1. 一般的 function
  2. method: 物件中的 function
  3. constructor: 建構物件的 function
  4. recursive: 呼叫自己的 function

(ES6 之後,加上的 arrow function ,請看《忍者2》)

Pure function

這是一個好語法的心法。

  1. Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
  2. Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).

意思是說

  1. function 的輸出永遠只與 function 的輸入相關 (取隨機數就不算 pure )
  2. 沒有 side effects (沒有使用全域變數,或儲存永久變數)

盡可能的讓 getter/setter 寫成 pure function ,將有助於你 debug。
這樣的設計,是 flux 在對狀態做更新與取用時,特別要注意的。(ex: vuex 的 mutation, getter)

要不要用 return

要不要 return 這件事,其實是值得討論的。
而且,我們其實早就習慣 return 了。

高中數學有學過

y = sin(x)

假想一下 sin() 的實作會是長怎樣?

function sin(radians) {
  const PI = 3.1415926588;
  while (parseInt(radians % PI)) {
    //...
  }

  //...
  return opposite / hypotenuse;
}

有 return 將斜邊與對邊相除之後的比例,回傳出來。
換句話說,比例 = sin 是不是等同於 y = sin(x) 的語意呢?

如果今天,我們寫了這樣正確的 sin 函數,但是命名不寫 sin 寫了 ChrisCalculated() 這樣是不是完全看不出來,函數實作內容了?如果不了解 sin 定義的人,看了也一定不懂裡面就是 sin 函數。

有時要不要 return 的問題,其實是命名的問題。
請務必隨時檢查整體概念性

兩個輸出要怎辦?

在 C++ 中,兩個輸出很簡單,輸出處傳入指標或參考即可。
兩者在函數的修改,都會影響其變數本體,而不是副本。

void Fun(input i1, output* o1, output* o2) {

}

void Fun(input i1, output& o1, output& o2) {

}

在 JavaScript 中,就將輸出變成物件再傳入。

function Fun(input i1, {output o1, output o2}) {

}

這兩種都不是好方法,因為變數傳入函數之後,一般來說都是預設不會修改其變數本身,或者它的修改都不會影響傳入參數之前的樣子 (傳入副本)。

這樣做確實會產生某些「變數與函數之間的耦合性」要特別寫註解告知伙伴。

Fun(i1, { o1, o2 }); // o1, o2: Fun1 的執行結果