咱們接下來會繼續優化咱們的 createStore
的模式,讓它使咱們的應用程序得到更好的性能。html
但在開始以前,咱們先用一節的課程來介紹一下一個函數式編程裏面很是重要的概念 —— 純函數(Pure Function)。編程
簡單來講,一個函數的返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,咱們就把這個函數叫作純函數。這麼說確定比較抽象,咱們把它掰開來看:數組
const a = 1 const foo = (b) => a + b foo(2) // => 3
foo
函數不是一個純函數,由於它返回的結果依賴於外部變量 a
,咱們在不知道 a
的值的狀況下,並不能保證 foo(2)
的返回值是 3。雖然 foo
函數的代碼實現並無變化,傳入的參數也沒有變化,但它的返回值倒是不可預料的,如今 foo(2)
是 3,可能過了一會就是 4 了,由於 a 可能發生了變化變成了 2。瀏覽器
const a = 1 const foo = (x, b) => x + b foo(1, 2) // => 3
如今 foo
的返回結果只依賴於它的參數 x
和 b
,foo(1, 2)
永遠是 3。今天是 3,明天也是 3,在服務器跑是 3,在客戶端跑也 3,無論你外部發生了什麼變化,foo(1, 2)
永遠是 3。只要 foo
代碼不改變,你傳入的參數是肯定的,那麼 foo(1, 2)
的值永遠是可預料的。服務器
這就是純函數的第一個條件:一個函數的返回結果只依賴於它的參數。函數式編程
一個函數執行過程對產生了外部可觀察的變化那麼就說這個函數是有反作用的。函數
咱們修改一下 foo
:性能
const a = 1 const foo = (obj, b) => { return obj.x + b } const counter = { x: 1 } foo(counter, 2) // => 3 counter.x // => 1
咱們把原來的 x
換成了 obj
,我如今能夠往裏面傳一個對象進行計算,計算的過程裏面並不會對傳入的對象進行修改,計算先後的 counter
不會發生任何變化,計算前是 1,計算後也是 1,它如今是純的。可是我再稍微修改一下它:測試
const a = 1 const foo = (obj, b) => { obj.x = 2 return obj.x + b } const counter = { x: 1 } foo(counter, 2) // => 4 counter.x // => 2
如今狀況發生了變化,我在 foo
內部加了一句 obj.x = 2
,計算前 counter.x
是 1,可是計算之後 counter.x
是 2。foo
函數的執行對外部的 counter
產生了影響,它產生了反作用,由於它修改了外部傳進來的對象,如今它是不純的。優化
可是你在函數內部構建的變量,而後進行數據的修改不是反作用:
const foo = (b) => { const obj = { x: 1 } obj.x = 2 return obj.x + b }
雖然 foo
函數內部修改了 obj,可是 obj
是內部變量,外部程序根本觀察不到,修改 obj
並不會產生外部可觀察的變化,這個函數是沒有反作用的,所以它是一個純函數。
除了修改外部的變量,一個函數在執行過程當中還有不少方式產生外部可觀察的變化,好比說調用 DOM API 修改頁面,或者你發送了 Ajax 請求,還有調用 window.reload
刷新瀏覽器,甚至是 console.log
往控制檯打印數據也是反作用。
純函數很嚴格,也就是說你幾乎除了計算數據之外什麼都不能幹,計算的時候還不能依賴除了函數參數之外的數據。
一個函數的返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,咱們就把這個函數叫作純函數。
爲何要煞費苦心地構建純函數?由於純函數很是「靠譜」,執行一個純函數你不用擔憂它會幹什麼壞事,它不會產生不可預料的行爲,也不會對外部產生影響。無論什麼時候何地,你給它什麼它就會乖乖地吐出什麼。若是你的應用程序大多數函數都是由純函數組成,那麼你的程序測試、調試起來會很是方便。