注: 學(xué)習(xí)程墨老師《深入淺出RxJS》的筆記
任何一種 Reactive Extension 的實(shí)現(xiàn),都包含一個(gè)操作符的集合路捧。操作符其實(shí)就是解決某個(gè)具體應(yīng)用問題的模式备籽。
為什么要有操作符
一個(gè)操作符是返回一個(gè)Observable對(duì)象的函數(shù)绸硕。
const result$ = source$.filter(x => x * 2 === 0).map(x => x * 2);
result$.subscribe(console.log);
在RxJS的世界中钠署,filter 和 map 這樣的函數(shù)就是操作符,每個(gè)操作符提供的只是一些通用的簡(jiǎn)單功能婆排,但是通過鏈?zhǔn)秸{(diào)用声旺,這些小功能可以組合在一起,用來解決復(fù)雜的問題段只。
操作符分類
RxJS v5版本自帶60做個(gè)操作符腮猖,按照功能分類,操作符可以分為:
- 創(chuàng)建類(creation)
- 轉(zhuǎn)化類 (transformation)
- 過濾類 (filtering)
- 合并類(combination)
- 多播類(multicasing)
- 錯(cuò)誤處理類(error Handling)
- 輔助工具類(utility)
- 條件分支類(conditional & boolean)
- 數(shù)學(xué)和合計(jì)類(mathmatial & aggregate)
靜態(tài)和實(shí)例分類
按照形式分類赞枕,也就是操作符的實(shí)現(xiàn)函數(shù)和Observable類的關(guān)系澈缺。
- 靜態(tài)操作符坪创,操作符是Obervable類的靜態(tài)函數(shù)
- 實(shí)例操作符,前提就是要有一個(gè)創(chuàng)建好的Observable對(duì)象姐赡,作為Observable的實(shí)例函數(shù)
// 靜態(tài)操作符
Observable.of
// 實(shí)例操作符
Observable.prototype.map
如何實(shí)現(xiàn)操作符
每個(gè)操作符都是一個(gè)函數(shù)莱预,不管實(shí)現(xiàn)什么功能,都必須考慮下面這些功能要點(diǎn):
- 返回一個(gè)全新的Observable 對(duì)象
- 對(duì)上游和下游的訂閱及退訂處理
- 處理異常情況
- 及時(shí)釋放資源项滑,有些和瀏覽器資源打交道的操作符依沮,或者有些和WebSocket資源關(guān)聯(lián),一定要在退訂的時(shí)候去掉掛在DOM上的這些事件處理函數(shù)枪狂,釋放WebSocket資源
手動(dòng)實(shí)現(xiàn)map操作符
版本1:
function map(project){
// 1.返回一個(gè)全新的Observable對(duì)象
return new Observable((observer)=> {
const sub = this.subscribe({
next: value => {
// 3.處理異常情況
try {
observer.next(project(value))
} catch (err) {
observer.error(err);
}
},
error: err => observer.error(err),
complete: () => observer.complete(),
});
// 2.訂閱和退訂處理
return {
unsubscribe: () => {
sub.unsubscribe();
}
}
});
}
this代表的就是上游的Observable對(duì)象危喉,箭頭函數(shù)中的this直接綁定于定義函數(shù)環(huán)境下的this,而不是執(zhí)行時(shí)指定的this州疾。
版本2:
需要把map和Observable 關(guān)聯(lián)起來辜限,可以使用打補(bǔ)丁方法,如下
Observable.prototype.map = (project) => {
}
但是有時(shí)候我們并不希望一個(gè)操作符影響所有的Observable對(duì)象严蓖,我們只想在某些代碼文件中使用薄嫡,自定義的ma方法把RxJS自帶的map覆蓋了。
為了解決這個(gè)問題颗胡,我們自定義的操作符只對(duì)指定的Observable對(duì)象可用岂座,這時(shí)就可以使用bind,如下
const result$ = map.bind(source$)(x => x * 2);
使用bind 的用法有個(gè)缺點(diǎn)杭措,就是上游Observable只能作為操作符函數(shù)的參數(shù),這樣鏈?zhǔn)經(jīng)]法用了钾恢,比如想要連續(xù)使用兩個(gè)map:
const result$ = map.bind( map.bind(source$)(x => x * 2) )(x => x + 1);
為了克服這個(gè)缺點(diǎn)手素,使用“綁定函數(shù)符”,綁定操作符以兩個(gè)冒號(hào)形式存在瘩蚪,運(yùn)行的時(shí)候運(yùn)行綁定操作符后面的函數(shù)泉懦,但是保證函數(shù)運(yùn)行時(shí)this是綁定操作符前面的對(duì)象。
const result$ = source$::map(x => x * 2)::map(x => x +1);
使用lift函數(shù)疹瘦,lift是Observable的實(shí)例函數(shù)崩哩,它會(huì)返回一個(gè)新的Observable對(duì)象
版本3:
使用obs$,替換掉了this言沐,因?yàn)橹苯邮褂胻his的函數(shù)不是純函數(shù)
function map(project){
return function(obs$){
// 1.返回一個(gè)全新的Observable對(duì)象
return new Observable((observer)=> {
const sub = obs$.subscribe({
next: value => {
// 3.處理異常情況
try {
observer.next(project(value))
} catch (err) {
observer.error(err);
}
},
error: err => observer.error(err),
complete: () => observer.complete(),
});
// 2.訂閱和退訂處理
return {
unsubscribe: () => {
sub.unsubscribe();
}
}
});
}
}
要使用map就有借用let邓嘹,let的作用是把map函數(shù)引入到鏈?zhǔn)秸{(diào)用之中,let起到連接上游下游作用险胰,真正的工作完全由函數(shù)參數(shù)map來執(zhí)行汹押。
const result$ = source$.let(map(x => x * 2));
lettable 和 pipeable
要使用map就要借助let這個(gè)操作符的力量,能夠被let當(dāng)做參數(shù)來使用起便,就叫做lettable棚贾。
在RxJS v5中引入lettable操作符之后依然保留打補(bǔ)丁和call方法的操作符模塊窖维,完全是為了向后兼容,長(zhǎng)遠(yuǎn)趨勢(shì)lettable操作符是方向妙痹。
pipe是Observable自帶一個(gè)新的操作符铸史,具備let的功能,使用pipe 無需像使用let一樣導(dǎo)入模塊怯伊,任何Observable對(duì)象都支持pipe琳轿。pipe還有管道功能,可以把多個(gè)lettable操作符串聯(lián)起來震贵,形成數(shù)據(jù)管道利赋。
const source$ = of(1, 2, 3);
const result$ = source$.pipe(
filter(x => x % 2 === 0),
map(x => x * 2),
);
result$ .subscribe(console.log);