【譯】JavaScript中純函數是什麼

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

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

image

檢測列表

一個函數必須知足以下兩點才能被稱之爲「純的」:git

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

讓咱們逐一展開。github

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

比較以下兩版代碼:編程

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,尤爲是在某些特定條件下?

哪一種方式更能勝任多線程系統環境,尤爲是這種系統環境會受時序因素影響?

顯然是第一種。

二、不產生反作用

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 請求,磁盤寫入,打印。
  • 能夠將函數輸入拷貝一份用於更改,不要去動原來的數據。
  • 擴展運算符語法(... 語法)是一種複製對象或者數據的簡便方法。
相關文章
相關標籤/搜索