30天精通RxJS(02):Functional Programming基本觀念

原做者:一名來自臺灣的小夥子,熱愛學習新技術,喜歡 JS 與 Functional Programming,熱衷於把困難的技術用簡單的語言闡述,歡迎來到個人文章。html

原文前端

什麼是Functional Programming?

Functional Programming是一種編程規範(programming paradigm),就像Object-oriented Programming(OOP)同樣,就是一種寫程序的方法論,這些方法論告訴咱們如何思考及解決問題。express

簡單說Functional Programming核心思想就是作運算處理,並用function來思考問題,例如像如下的算數表達式:編程

(5 + 6) - 1 * 3
複製代碼

咱們能夠寫成數組

const add = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b

sub(add(5, 6), mul(1, 3))
複製代碼

咱們把每一個運算包成一個個不一樣的function,並用這些function組合出咱們要的結果,這就是最簡單的Functional Programming。bash

Functional Programming 基本要件

跟OOP同樣不是全部的語言都支持FP,要可以支持FP的語言至少須要符合函數爲一等公民的特性。ide

函數爲一等公民(First Class)

一等公民就是指跟其餘數據類型具備同等地位,也就是說函數可以被賦值給變量,函數也可以被看成參數傳入另外一個函數,也可看成一個函數的返回值函數

函數可以被賦值給變量

var hello = function() {}
複製代碼

函數能被看成參數傳入

fetch('www.google.com')
.then(function(response) {}) // 匿名 function 被傳入 then()
複製代碼

函數能被看成返回值

var a = function(a) {
	return function(b) {
	  return a + b;
	}; 
	// 能夠返回一個 function
}
複製代碼

Functional Programming重要特性

Expression, no Statement

Functional Programming都是表達式(Expression)不會是聲明式(Statement)。基本區分表達式與聲明式:單元測試

表達式是一個運算過程,必定會有返回值,例如執行一個function學習

add(1,2)
複製代碼

聲明式 則是表現某個行爲,例如一個賦值給一個變數

a = 1;
複製代碼

有時候表達式也可能同時是合法的聲明式,這裏只講基本的判斷方法。若是想更深刻了解其中的差別,能夠看這篇文章 Expressions versus statements in JavaScript

因爲Functional Programming最先就是爲了作運算處理無論I/O,而Statement一般都屬於對系統I/O的操做,因此FP很天然的不會是Statement。

固然在事務中不可能徹底沒有I/O的操做,Functional Programming只要求對I/O操做限制到最小,不要有沒必要要的I/O行爲,儘可能保持運算過程的單純。

Pure Function(純函數)

Pure function是指一個function給予相同的參數,永遠會回傳相同的返回值,而且沒有任何顯著的反作用(Side Effect)

舉個例子:

var arr = [1, 2, 3, 4, 5];

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]
複製代碼

這裏能夠看到slice無論執行幾回,返回值都是相同的,而且除了返回一個值(value)以外並無作任何事,因此slice就是一個pure function。

var arr = [1, 2, 3, 4, 5];

arr.splice(0, 3); // [1, 2, 3]

arr.splice(0, 3); // [4, 5]

arr.slice(0, 3); // []
複製代碼

這裏咱們換成用splice,由於splice每執行一次就會影響arr的值,致使每次結果都不一樣,這就很明顯不是一個pure function。

Side Effect(反作用)

Side Effect是指一個function作了跟自己運算返回值沒有關係的事,好比說修改某個全局變數,或是修改傳入參數的值,甚至是執行console.log都算是Side Effect。

Functional Programming強調沒有Side Effect,也就是function要保持純粹,只作運算並返回一個值,沒有其餘額外的行爲。

這裏列舉幾個前端常見的Side Effect,但不是所有

  • 發送http request

  • 在畫面輸出或是log

  • 得到使用者input

  • Query DOM對象

Referential transparency(引用透明)

前面提到的pure function無論外部環境如何,只要參數相同,函數執行的返回結果一定相同。這種不依賴任何外部狀態,只依賴於傳入的參數的特性也稱爲引用透明(Referential transparency)

利用參數保存狀態

因爲最近很紅的Redux使我能很好的舉例,讓你們瞭解什麼是用參數保存狀態。瞭解Redux的開發者應該會知Redux的狀態是由各個reducer所組成的,而每一個reducer的狀態就是保存在參數中!

function countReducer(state = 0, action) {
// ...
}
複製代碼

若是你跟Redux不熟能夠看下面遞迴的例子

function findIndex(arr, predicate, start = 0) {
    if (0 <= start && start < arr.length) {
        if (predicate(arr[start])) {
            return start;
        }
        return findIndex(arr, predicate, start+1);
    }
}
findIndex(['a', 'b'], x => x === 'b'); // 找數組中 'b' 的 index
複製代碼

這裏咱們寫了一個findIndex用來找數組中的元素位置,咱們在findIndex中故意多塞了一個參數用來保存當前找到第幾個index的狀態,這就是利用參數保存狀態!

這邊用到了遞歸,遞歸會不斷的調用本身,製造多層stack frame,會致使運算速度較慢,而這一般須要靠編譯器作優化!

那JS有沒有作遞歸優化呢?恭喜你們,ES6提供了尾調用優化(tail call optimization),讓咱們有一些手法可讓遞歸更有效率!

Functional Programming優點

可讀性高

當咱們經過一系列的函數封裝數據的操做過程,代碼能變得很是的簡潔且可讀性極高,例以下面的例子

[9, 4].concat([8, 7]) // 合併數組
      .sort()  // 排序
      .filter(x => x > 5) // 過濾出大於 5 的
複製代碼

可維護性高

由於Pure function等特性,執行結果不依賴外部狀態,且不會對外部環境有任何操做,使Functional Programming能更好的排錯及編寫單元測試。

易於並行/平行處理

Functional Programming易於作並行/平行(Concurrency/Parallel)處理,由於咱們基本上只作運算不碰I/O,再加上沒有Side Effect的特性,因此較不用擔憂deadlock等問題。

今日小結

今天講了Functional Programming的基本特性,及其優點。如今越來越多的Library用到了FP的觀念,JS也愈來愈多Functional的函數庫,例如:Lodash,Underscore,lazy,Ramda。瞭解FP的基本觀念有助於咱們在學習其餘Library更容易上手,也能使咱們寫出更好的代碼,但願各位讀者有所收穫,如有任何疑問歡迎在下方留言給我!

相關文章
相關標籤/搜索