ReactiveX combines the
Observer pattern
with theIterator pattern
andfunctional programming
with collections to fill the need for an ideal way of managing sequences of events. ReactiveX將觀察者模式、迭代器模式和函數編程與集合結合起來,以知足管理事件序列的理想方式的須要。html
根據官方定義,RxJS 是基於觀察者模式和迭代器模式以函數式編程思惟來實現的,那麼咱們先了解一下這幾個概念。express
Functional Programming
是一種編程範式(programming paradigm
),就像Object-oriented Programming(OOP)
同樣,就是一種寫程式的方法論,這些方法論告訴咱們如何思考及解決問題。編程
函數式編程關心數據的映射,命令式編程關心解決問題的步驟.bash
這裏的映射就是數學上函數的概念——一種東西和另外一種東西之間的對應關係, 簡單說Functional Programming
核心思想就是作運算處理,並用function 來思考問題.markdown
所謂一等公民是指跟其它對象具備同等的地位,也就是說函數可以被賦值給變量,也可以被看成參數傳入另外一個函數,也可看成一個函數的返回值。數據結構
// 函數可以被賦值給變量 var hello = function() {} // 函數看成參數傳入另外一個函數 fetch('www.google.com') .then(function(response) {}) // 匿名 function 被傳入 then() // 看成一個函數的返回值 var a = function(a) { return function(b) { return a + b; }; } 複製代碼
Functional Programming
都是表達式(Expression
)不會是語句(Statement)。 基本區分表達式與語句:併發
function
, 聲明一個變量。有時候表達式也可能同時是合法的語句,這裏只講基本的判斷方法。若是想更深刻了解其中的差別,能夠看這篇文章Expressions versus statements in JavaScript dom
純函數是這樣一種函數,即相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用(side effect
)ide
舉個簡單的例子,slice
和splice
:函數式編程
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
無論執行幾回,返回值都是相同的,而且除了返回一個值以外並無作任何事,因此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
)
反作用指一個function
作了跟自己運算返回值沒有關係的事,好比說修改某個全域變數,或是修改傳入參數的值,甚至是執行console.log
都算是反作用。
Functional Programming
強調沒有反作用,也就是function
要保持純粹,只作運算並返回一個值,沒有其餘額外的行爲。
這裏列舉幾個常見的反作用:
歸納來說,只要是跟函數外部環境發生的交互就都是反作用——這一點可能會讓你懷疑無反作用編程的可行性。函數式編程的哲學就是假定反作用是形成不正當行爲的主要緣由, 這並非說,要禁止使用一切反作用,而是說,要讓它們在可控的範圍內發生。
Pure function
等特性,執行結果不依賴外部狀態,且不會對外部環境有任何操做,這使得單元測試和調試都更容易觀察者模式,即發佈-訂閱模式,它定義了一個一對多的依賴關係,讓一個或多個觀察者對象監聽一個主題對象。這樣一來,當被觀察者狀態發生改變時,須要通知相應的觀察者,使這些觀察者對象可以自動更新。
主題是觀察者觀察的對象,一個主題必須具有下面三個特徵。
當主題發生變化,收到通知後進行具體的處理
這裏舉一個例子來講明,牛奶送奶站就是主題,訂奶客戶爲監聽者,客戶從送奶站訂閱牛奶後,會天天收到牛奶。若是客戶不想訂閱了,能夠取消,之後就不會收到牛奶。
根據上面的說明,咱們能夠簡單實現一個被觀察者:
class Subject { constructor() { this.observerCollection = []; } registerObserver(observer){ this.observerCollection.push(observer) } unRegisterObserver(observer){ this.observerCollection.splice(this.observer.findIndex(observer), 1) } notifyObservers(message){ this.observerCollection.forEach(observer => { observer.notify(message); }) } } 複製代碼
迭代器模式(Iterator)提供了一種方法順序訪問一個集合對象中各個元素,而又不暴露該對象的內部表示,迭代器模式能夠把迭代的過程從業務邏輯中分離出來,在使用迭代器模式以後,即便不關心對象的內部構造,也能夠按順序訪問其中的每一個元素。
Iterator
的遍歷過程是這樣的:
建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
第一次調用指針對象的next
方法,能夠將指針指向數據結構的第一個成員。
第二次調用指針對象的next
方法,指針就指向數據結構的第二個成員。
不斷調用指針對象的next
方法,直到它指向數據結構的結束位置。
可參考ES6系列--7. 可迭代協議和迭代器協議中關於迭代器的介紹。
JavaScript
中像 Array、Set、Map
等都屬於內置的可迭代類型, 能夠經過 iterator
方法來獲取一個迭代對象,調用迭代對象的 next
方法將獲取一個元素對象,以下示例:
var arr = [1, 2, 3]; var iterator = arr[Symbol.iterator](); iterator.next(); // { value: 1, done: false } iterator.next(); // { value: 2, done: false } iterator.next(); // { value: 3, done: false } iterator.next(); // { value: undefined, done: true } 複製代碼
遍歷迭代器可使用下面的方法。
while(true) { let result; try { result = iterator.next(); } catch (error) { handleError(error); // 錯誤處理 } if (result.done) { handleCompleted(); // 已完成以後的處理 } doSomething(result.value); } 複製代碼
上面的代碼主要對應三種狀況:
next
方法能夠將元素一個個的返回,這樣就支持了返回屢次值。done
爲true
。next
方法執行時報錯,則會拋出 error
事件,因此能夠用 try catch
包裹 next
方法處理可能出現的錯誤。下一篇開始介紹Observable 和 observer。