iT邦幫忙

2022 iThome 鐵人賽

DAY 4
0
Modern Web

致 JavaScript 開發者的 Functional Programming 新手指南系列 第 4

Day 4 :JavaScript 型別與他們的地雷(1): 基本型別與物件

  • 分享至 

  • xImage
  •  

在了解 FP 與 JavaScript 的歷史之後,有沒有覺得:「咦?設計模式其實也沒有離自己那麼遠嘛!」在繼續深入了解 FP 之前,讓我們來看看 JavaScript 這門語言的一些特性:

基本型別資料取用

在 JavaScript 中,我們會使用 letconst 來進行變數的存放。

假設今天我們要設計一個小功能,是透過遠端 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 另外一個特性:物件與物件的傳分享特性。


上一篇
Day 3:初探設計典範(2):FP 簡介(修正版)
下一篇
Day 5:JavaScript 型別與他們的地雷(2):{ } === { } ?
系列文
致 JavaScript 開發者的 Functional Programming 新手指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言