---
title: "Subject、Observable和Observer"
date: "2026-04"
category: "Angular"
tags: ["Angular"]
description: "Observable的pipe那裡説到了供水，但那邊只是被動的供水——如果我想要中間往裡面加水並讓所有吃水者都喝上新的水呢？比如從地下水換成山泉水，帶有甘甜的滋味（嗯......）..."
source: "https://enriquemark.com/zh-hant/posts/Subject%E3%80%81Observable%E5%92%8CObserver"
---

[Observable的pipe](/zh-hant/posts/Observable的pipe)那裡説到了供水，但那邊只是被動的供水——如果我想要中間往裡面加水並讓所有吃水者都喝上新的水呢？比如從地下水換成山泉水，帶有甘甜的滋味（嗯......）。這裡就得用`Subject`。不過這個地方我得稍微分出來說個東西，那就是`Observable`和`Observer`，這倆在用的時候都會有一個`next`出來，很容易搞混。`next`即操作水塔，同時也代表去水塔接水，也就是釋出方定義傳出資料和訂閲放接受資料。

```ts
// 指定範型 <number>，表示這個流會發送數字
const observable = new Observable((subscriber: Subscriber<number>) => {
  subscriber.next(1); // 這裡的 next 是 Subscriber 實作的方法
  subscriber.complete();
});

// 訂閱測試
observable.subscribe({
  next: (val) => console.log(val),
  complete: () => console.log('Done')
});
```

`Subscriber<T>`繼承了`Observer<T>`這個介面，而後者約定了必須要實現`next`、`error`和`complete`三個方法：

```TS
interface Observer<T> {
  next: (value: T) => void;
  error: (err: any) => void;
  complete: () => void;
}
```

而`next`的傳入元素在定義`observable`的時候通過`subscriber.next(1)`直接給定了，這也是為什麼下游直接`next: (val) => console.log(val)`能取到值的原因，它取到的正是這裡的內容。這裡搞明白了，我們就知道為啥`Subject`不同，因為它自身即是一個`Observable`，同時也是一個`Observer`。

```ts
// 1. 建一個水塔，默認是地下水
const mySubject = new Subject<string>();
// 2. A 跑來接水
mySubject.subscribe(data => console.log('A 收到:', data));

// 3. 把山泉水接入進水塔！
mySubject.next('山泉水'); 
// 主控台印出: A 收到: 山泉水
```

這就意味著，此物件的操作不再只是單純的取水，還可以決定往裡面加什麼水。只要加了，後續所有的消費者，都會受到影響。所以實務上一般都不太會把`Subject`直接暴露出來，否則不可控情況下，下游會把整個資料流改的面目全非。

不過`Subject`具備一個「多播」 (Multicast)的特性，會導致其釋出的時候回調全部下游訂閱者（執行了`subscribe`方法的物件）的`next`，並把`mySubject.next('山泉水')`這個資料塞過去。也就是説，如果存在此時還未被觸發`subscribe`的物件，它不僅拿不到這個值，其他值也拿不到。就跟一個高壓廣播水管一樣，來了就全部強行噴出，之後就關閘，一滴水也不給你。這會導致的顯著問題就可以見以下場景：事件觸發機制下的操作，使用者點選之後才會去`subscribe`。那此時晚來者就會兩手空空，什麼都拿不到。

`BehaviorSubject`這時候就派上用場，它是一個持續作用的`Subject`，不僅會在第一輪塞給所有人山泉水，還會在後面有人來的時候也給他一份，可以説是一個「熱情的給水人」，不管什麼事來，你要了他就給，跟`Subject`那樣過期不候的高冷性格相對。實際使用的時候，大部分也都是用這個。

但問題來了，若有人就是嘴欠，嫌棄山泉水不行，非要喝地下水怎麼辦？當前的供水單一，只會讓下游取到山泉水——`ReplaySubject`就派上用場了。如果説前兩者，一個是無狀態的：`Subject`晚來了兩手空空；一個是隻有最新狀態：`BehaviorSubject`無論什麼時候來都給你最新的（山泉水）；`ReplaySubject`則是包含了過去和現在指定範圍內的全部狀態。它可以讓下游消費者取到過去的N個資料，即讓後續的取水者無論山泉水還是地下水（取決呼叫時指定的範圍），都能拿到手。如果你真的就喜歡喝地下水，自己把山泉水丟棄即可，或者你都想要，全部喝了也無所謂。總之就是將處置權交給下游，水塔只管供水，不管你怎麼處置。
