【譯】JavaScript中純函式是什麼

飛翔的大白菜發表於2019-01-24

純函式是程式函數語言程式設計語言中原子構建塊(最簡單的可重用程式碼構建塊)。簡單和易測試性的特點使其備受推崇。

本文將提供一個快速檢測列表,用於判斷一個函式是否為純函式。

image

檢測列表

一個函式必須滿足如下兩點才能被稱之為“純的”:

  1. 相同的輸入 總是 返回相同的輸出
  2. 不產生副作用

讓我們逐一展開。

1、相同的輸入 => 相同的輸出

比較如下兩版程式碼:

const add = (x, y) => x + y;
add(2, 4); // 6
複製程式碼
let x = 2;
const add = (y) => {
  x += y;
};
add(4); // x === 6 (the first time)
複製程式碼

純函式 = 穩定的結果

不管你在何時何地呼叫第一個例子中的函式,得到的結果僅僅依賴於傳入的引數。

如傳入 24,得到結果 6

輸出不受其他任何因素影響。

非純函式 = 不穩定的結果

第二個例子中的函式什麼也不返回。它的工作依賴於 共享狀態,遞增了一個外部變數。

這樣的話就是開發者的噩夢了。

共享狀態 具有時序依賴性。在不同的時機呼叫,會得到不一樣的結果。第一次得到的結果是 6,第二次是 10 以此類推。

哪一版更加合理?

哪種方式更不容易產生 bug,尤其是在某些特定條件下?

哪種方式更能勝任多執行緒系統環境,尤其是這種系統環境會受時序因素影響?

顯然是第一種。

2、不產生副作用

image

這一檢測點本身也是一個檢測列表。如下是一些副作用:

  1. 更改輸入
  2. console.log
  3. HTTP 請求(AJAX、fetch)
  4. 更改檔案系統
  5. DOM 查詢

基本上函式中執行的任何操作都與最終計算輸入結果無關,僅僅依賴於傳入的引數。

推薦大家看看 Uncle Bob Martin 對於系統狀態問題的講解視訊。大約從 15min 處開始。

如下是產生了副作用的不純函式。

還湊合

const impureDouble = (x) => {
  console.log('doubling', x);
  return x * 2;
};
const result = impureDouble(4);
console.log({ result });
複製程式碼

雖說 console.log 產生了副作用,但實際上它沒什麼不良影響。我們仍然能保證相同的輸入得到相同的輸出

然而接下來這個就不一樣了,可能會導致一些問題。

“純度汙染”改變了物件

const impureAssoc = (key, value, object) => {
  object[key] = value;
};
const person = {
  name: 'Bobo'
};
const result = impureAssoc('shoeSize', 400, person);
console.log({
  person,
  result
});
複製程式碼

函式中的賦值語句,已經讓變數 person 發生了不可逆的改變。

共享狀態意味著 impureAssoc 的影響不那麼明顯。要理解它對系統產生的副作用,需跟蹤它涉及到的每一個變數,瞭解這些變數的歷史變化軌跡。

共享狀態 = 時序依賴

impureAssoc 變純很簡單,我們只要返回一個包含所需屬性的新物件即可。

使它變純

const pureAssoc = (key, value, object) => ({
  ...object,
  [key]: value
});
const person = {
  name: 'Bobo'
};
const result = pureAssoc('shoeSize', 400, person);
console.log({
  person,
  result
});
複製程式碼

現在 pureAssco 返回一個明確可測試的結果,不再擔心它會在其他地方偷偷地變了。

想將它變純,你甚至可以如下這麼做:

另一種變純之法

const pureAssoc = (key, value, object) => {
  const newObject = { ...object };
  newObject[key] = value;
  return newObject;
};
const person = {
  name: 'Bobo'
};
const result = pureAssoc('shoeSize', 400, person);
console.log({
  person,
  result
});
複製程式碼

直接更改函式的輸入是有些風險的,但改變輸入的副本就沒什麼問題了。不管在哪裡呼叫,我們都將得到一個穩定可測試的函式。

總結

image

  • 不產生副作用,並且相同的輸入總是返回相同的輸出,那麼這個函式是純函式。
  • 副作用包括但不限於:更改輸入,HTTP 請求,磁碟寫入,列印。
  • 可以將函式輸入拷貝一份用於更改,不要去動原來的資料。
  • 擴充套件運算子語法(... 語法)是一種複製物件或者資料的簡便方法。

相關文章