在了解 FP 與 JavaScript 的歷史之後,有沒有覺得:「咦?設計模式其實也沒有離自己那麼遠嘛!」在繼續深入了解 FP 之前,讓我們來看看 JavaScript 這門語言的一些特性:
在 JavaScript 中,我們會使用 let
、const
來進行變數的存放。
假設今天我們要設計一個小功能,是透過遠端 API 知道今天的值日生是誰,此時透過 API 撈回一組資料,但是我們只要其中的某個基本型別的值(因為我們只需要知道誰今天要值日),此時的程式碼可能會是長這樣:
const apiPath = "xxx";
let studentOnDuty = "";
// 假設 axios 是某個封裝好的遠端請求實體
axios.get(apiPath).then(({ data }) => {
studentOnDuty = data;
console.log(`今天的值日生是: ${studentOnDuty}`);
});
// => 今天的值日生是小明
但萬一今天有個同事在同一個檔案中,也要透過這個 API 建立跟值日生有關的功能怎麼辦?
const apiPath = "xxx";
let studentOnDuty = "";
axios.get(apiPath).then(({ data }) => {
studentOnDuty = data;
console.log(`今天的值日生是: ${studentOnDuty}`);
});
const apiPath2 = "xxx";
axios.get(apiPath2).then(({ data }) => {
studentOnDuty = data;
console.log(`今天的值日生學號是: ${studentOnDuty}`);
});
假設我的同事透過另外一個 API 取得值日生的學號,並使用同一個 studentOnDuty
的變數,把值日生的學號印出來⋯⋯咦?那我之後如果再想要取得值日生的名字不就被覆蓋掉了嗎?
雖然這個狀況我們可以透過更嚴格命名變數、取用方式,或是其他手段來做到,但不可避免的是,只要是需要動態操作透過並且夠過 let
關鍵字宣告的變數,就是會有輕易能被覆寫的可能性。
在 JavaScript 中,除了基本型別以外的型別都是物件,像是 Promise
、 { }
、[ ]
、Symbol
等。
以前端開發中,我們會透過 API 拿到許多零碎但是關鍵的資料,然後再把這些資料組合成我們想要的樣子,舉例來說,這一次我想要把每個班級的所有值日生資料都一次性拿到,然後把這些值日生的資訊都印出來:
const apiPath3 = "xxx";
let studentOnDutyList = [];
axios.get(apiPath3).then(({ data }) => {
studentOnDutyList = data;
studentOnDutyList.forEach(({name, classNum, number}) => console.log(`我是 ${classNum} 班的${name},學號是${number}`));
});
// => 我是 1 班的王小花,學號是 110110
// => 我是 2 班的謝小明,學號是 110210
此時你在寫程式的過程中,發現:「咦!這隻 API 是舊的,學號沒有更新,全部都少了一!」
現在要實作另外一個功能,就必需把學號都加一,但又不能影響到其他的程式碼,比如說:有些地方需要用到舊的學號,有些地方需要用到新的!
所以我們現在新增另外一個變數 newStudentOnDutyList
,用來儲存修改完的的資料,原本用來儲存原始資料 studentOnDutyList
的變數則一動不動。
const apiPath3 = "xxx";
let studentOnDutyList = [];
let newStudentOnDutyList = [];
axios.get(apiPath3).then(({ data }) => {
studentOnDutyList = data;
studentOnDutyList.forEach(({name, classNum, number}) => console.log(`我是 ${classNum} 班的${name},學號是${number}`));
newStudentOnDutyList = studentOnDutyList.map((data) => data.number+=1);
newStudentOnDutyList.forEach((number) => console.log(`學號:${number}`));
});
// output 1
//我是 1 班的王小花,學號是 110110
//我是 2 班的謝小明,學號是 11021
// output 2
//學號:110111
//學號:110211
嗯嗯看起來很合理,該出現的都有出現,接著我們來看看此時的 studentOnDutyList
有沒有長得一樣:
console.log(studentOnDutyList);
/* [
{
"name": "王小花",
"classNum": 1,
"number": 110111
},
{
"name": "謝小明",
"classNum": 2,
"number": 110211
]
*/
咦?怎麼好像跟我們預期的不同,原本學號不該被加一的地方,卻也跟著變動了?明明程式碼的地方看起來一樣呀?
這邊的範例,我特別使用「不好」的做法,在這邊我們暫且不探討什麼是比較好的做法,只是要跟大家分享其實 JavaScript 這門語言的特性就是「很容易改到不該改的東西」,尤其是在沒有足夠開發經驗或是對這門語言不夠熟悉的狀況下,特別容易踩到一些奇奇怪怪的地雷,尤其是值的覆蓋與不小心改到共用資料或狀態。
當這樣的狀況頻繁發生,可能就會讓網頁服務很難維護,當元件要共用時,就會出現改 A 壞 B,修好了 B ,C 又壞的狀況出現。
而 FP 有非常好的設計方式可以來解決這個問題,但在進入到 FP 的介紹前,我們還要再多踩幾個 JavaScript 的地雷。
在下一個章節中,我們要來聊聊 JavaScript 另外一個特性:物件與物件的傳分享特性。