函數式編程裏面有一個重要的概念,純函數,即怎樣的輸入有怎樣的輸出,並且不依賴於外部變量,同時函數也不會產生任何可觀察的副作用。那麼什麼是函數副作用呢?
函數副作用是指函數在正常工作任務之外對外部環境所施加的影響。
具體地說,函數副作用是指函數被調用,完成了函數既定的計算任務,
1,但同時因爲訪問了外部數據,尤其是因爲對外部數據進行了寫操作,從而一定程度地改變了系統環境。
2,函數的副作用也有可能是發生在函數運行期間,由於對外部數據的改變,導致了同步運行的外部函數受到影響。
總結:重要的一點是有沒有改變系統環境。
比如一個純函數在運行期間調用了另外一個純函數,雖然調用了外部變量,但是該函數仍然是純函數,因爲該函數並沒有改變系統環境。
什麼是可觀察的副作用:
在函數內部與其外部的任意交互。這可能是在函數內修改外部的變量
常見的副作用實例:
-
進行一個 HTTP 請求
-
Mutating data
-
輸出數據到屏幕或者控制檯
-
DOM 查詢/操作
-
Math.random()
-
獲取的當前時間
副作用的意義
副作用從名字上看似乎是不好的,但是實際應用中副作用有很大的意義。
在探討副作用的意義之前,我們先來看下純函數的意義。
1,函數式編程中大量使用純函數
函數式編程可以接收函數作爲入參,並且可以返回函數,從代碼的可維護性上講,函數式編程最大的好處是引用透明,即函數運行的結果只依賴於輸入的參數,而不依賴於外部狀態
函數式編程的精髓:業務系統模型無狀態。模型的好壞,直接影響到代碼的正確性、可靠性、穩定性,以及是否需要996。
2,方便代碼重構
由於純函數只和入參有關,這時候重構該函數時對系統幾乎沒有影響
3,方便測試,可預測性增強,降低代碼管理的難度
不需要考慮上下文,只考慮輸入輸出
4,健壯性
改變純函數的執行次序,並不會對結果造成影響
實例:redux的reducer出=純函數
reducer每次返回一個新的狀態,而不是去改變舊的狀態,避免了對象的深比較,而深比較是比較昂貴的。
與之相對的是函數副作用
函數副作用的良性意義:
1,通過指針和引用參數,主動讓被調函數獲得外部數據訪問權,以便提高數據處理的性能,比如實現緩存機制
2,改變全局變量,比如計數,根據全局變量的不同的值,調用不同的接口
3,通過返回指針或引用的做法,獲得共享堆空間,以簡化計算工作。
緩存設計實例:
比如,有函數getRseource(path)根據獲得資源,如果沒有副作用,每次都要執行存儲,有了副作用之後(外部變量),就可以簡化操作
var UtilFiles;
getFileList: function(path) {
if (UtilFiles) return UtilFiles;
if (!fs.existsSync(path)) {
throw new Error("can't find pages directory.");
}
var files = fs.readdirSync(path);
if (!files || files.length === 0) {
throw new Error("can't find any page");
}
files = files.filter(function(f) {
return fs.existsSync(pageDir + "/" + f + "/app.js");
});
if (files.length === 0) {
throw new Error("can't find any page");
}
UtilFiles = files;
return files;
},
說明:
- 通過var創建的全局變量(任何函數之外的程序中創建)是不能被刪除的。
- 無var創建的隱式全局變量(無視是否在函數中創建)是能被刪除的。
隱式全局變量並不是真正的全局變量,但它們是全局對象的屬性
參考:https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E5%89%AF%E4%BD%9C%E7%94%A8/22723425?fr=aladdin
https://www.liaoxuefeng.com/article/1260118907809920