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 來思考問題.數據結構
所謂一等公民是指跟其它對象具備同等的地位,也就是說函數可以被賦值給變量,也可以被看成參數傳入另外一個函數,也可看成一個函數的返回值。併發
// 函數可以被賦值給變量
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)。 基本區分表達式與語句:dom
function
, 聲明一個變量。有時候表達式也可能同時是合法的語句,這裏只講基本的判斷方法。若是想更深刻了解其中的差別,能夠看這篇文章Expressions versus statements in JavaScript ide
純函數是這樣一種函數,即相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用(side effect
)函數式編程
舉個簡單的例子,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。