我們需要注意的 immutable 操作

螞蟻金服資料體驗技術發表於2018-03-14

作者簡介:hustcc 螞蟻金服·資料體驗技術團隊

immutable 是什麼?不變的、一成不變的。在 Javascript 中一般指一個變數在經過一個 function 處理之後,可以保持入引數據不變。

一、什麼是 immutable?

舉個真實日常例子:前兩天,在業務程式碼中,需要獲得資料的中位數,並使用 echarts 繪製出中位線,輔助分析。寫程式碼之前,先找了一下中位線的定義。

對於有限的數集,可以通過把所有觀察值高低排序後找出正中間的一個作為中位數。如果觀察值有偶數個,通常取最中間的兩個數值的平均值作為中位數。

哦,挺簡單,於是奮筆疾書(其實是 github 上的程式碼片段

/** * 求取陣列中的中位數 * [1, 2, 3, 4, 4] ->
3 * [1, 2, 3, 4, 5, 6] ->
3.5 */
const calculateMedian(arr) =>
{
 
// 1. 排序  
arr.sort((a, b) =>
a - b);
 
const half = Math.floor(arr.length / 2);
// 2. 按照中位數定義計算  
// 奇數長度則中間值,偶數長度則中間兩個數的平均值  
return arr.length % 2 !== 0 ? arr[half] : (arr[half - 1] + arr[half]) / 2.0;

}複製程式碼

這段程式碼的問題在於函式是 mutable 的:

當經過函式 calculateMedian 求取陣列 [5, 4, 3, 2, 1] 的中位數的時候,導致入引數組變成了 [1, 2, 3, 4, 5]。導致後續的程式碼拿到的陣列,都是經過排序的,資料都是混亂的。

上述程式碼只需要修改一個地方即可:

const newArr = [].concat(arr).sort((a, b) =>
a - b);
複製程式碼

其中 [].concat(arr) 達到的效果就是程式碼的 immutable。

通過這樣的一個實際例子,我想應該很容易理解 mutable 程式碼造成的影響是什麼?

  • 資料汙染:經過一個方法,資料就變化了,多麼可怕。
  • 資料不可控:資料不僅僅是被修改,而且我們沒法控制資料被修改的規律。
  • debug 黑洞:一般的 bug,如果瞭解業務,可能不用 debugger 就能定位一二;mutable 產生的 bug,沒辦法,新增程式碼一句句 F10 吧。

二、常見的原因

很多情況下,道理我們都懂,但是還是會不經意寫出 mutable 的程式碼。分為幾種情況,看起來清晰一些吧!

1. 意識不清晰

很多開發者在寫程式碼的時候,沒有從全域性勝寒意識到函式必須保證 immutable,只在乎自己的函式輸入輸出符合要求。

開發者需要有時刻警惕的意識,一旦涉及到 object 的操作,先提醒自己注意不可變。特別是對於公共模組的開發,開源專案的開發,養成“變道就打方向燈的好習慣”。

2. 函式理解不清晰

Array 常見的函式 push、pop、shift 等,一般都能理解它們是會修改原陣列資料,是 mutable 的函式。

但是對於上述例子中的 sort 函式應該就是理解上容易產生歧義(自己之前一直以為是生成一個新的陣列,寫完這篇,我去搜尋了整個業務程式碼)。同樣的,容易產生歧義的函式包括:

  • sort
  • reverse
  • fill
  • splice
  • delete

注意:Array 中 immutable 函式很多,以上幾個函式是功能上容易誤解的。

3. 使用“不靠譜”的開源庫

GitHub 真是個好東西,只要你能想到的程式碼,基本都有參考的,特別是 JavaScript 語言的。

但是你永遠不知道你使用的輪子是不是僅僅是一個課堂作業練手的。在使用一個開源模組的時候,需要觀察:

  • 單測是否完善:不是指單測是不是綠色(pass),而是看單測程式碼是否靠譜

在完善的單測可以看到:

  1. 專案實際的使用方式,這些很可能是 README 中無法完整寫出來的東西。
  2. 理解專案的模組劃分,組織架構。
  3. 開發者在專案程式碼中扣住的開發細節點。

比如,如果 immutable 是專案的一個重要特性之一,那麼在單測程式碼中一定會反映出來。

  • npm 下載量

就像在網上購物一樣,比較懶的購物者,直接買訂單數最多的爆款。

  • issues

可以關注在 issue 處理速度、issue 中搜尋關鍵字,可以大概知道是否存在這些問題。

  • 何妨 review 一下

如果程式碼簡單,可以簡單 review 一下,抓住程式碼實現的關鍵點。

三、如何寫 immutable 的程式碼

既然要寫 immutable 的程式碼,那我們應該怎麼做?

1. 靠譜開源專案

  1. immutability-helper:基於原始 Array 和 Object 的 immutable 操作,很好用。
  2. js-joda:日期 Date immutable 方法。
  3. immutable-js:Facebook 的 immutable 模組,定義了新的資料結構,使用成本相對高一點點。
  4. immutability-helper-x:包裝 immutable-helper,使用更友好。
  5. immu:基於 Proxy 的 immutable 模組,思路新穎,但是相容性差一點。

2. 單測約束

針對開頭的 calculateMedian 函式,寫一個單測:

describe('calculateMedian', () =>
{
test('immutable', () =>
{
 
const a = [5, 4, 3, 2, 1];
const aClone = a.slice();
expect(calculateMedian(a)).toBe(3);
// 保證不能被修改! expect(a).toEqual(aClone);
 

});

});
複製程式碼

單測約束之後,以後其他人修改這麼程式碼導致 mutable 的時候,也會 ci 報錯的,保證這個方法的長治久安。

除此之外,開啟 Eslint 工具,也可以對 Object 的 mutable 自動告警。

3. 解構和 spread 語法

解構是 spread 語法是 ES6 中的新語法,也是我個人用的非常多的,一般程式碼的 immutable 寫法,都可以滿足。

  • Object 解構與 spread

比如我們有一個使用者資訊 A(幾十項資訊),然後需要建立一個使用者資訊 userB,除 id、name 不一樣之外,其他都和 userA 一致。

const userA = { 
id: 1, name: 'hustcc', addr: 'Hangzhou', birth: '1992-08-01', // ...  
// 很多的資料不列出了
};
// 結構和 spread 寫法建立 userBconst userB = {
...userA, id: 2, name: 'ProtoTeam',
};
複製程式碼
  • 陣列 immutable 操作
const arr = [1, 2, 3, 4, 5];
// push 操作const arrPush = [ ...arr, 6,];
// shift 操作const [e, ...arrShift] = arr;
// concatconst arrConcat = [...arr, ...arr];
複製程式碼

四、最後

在 react + redux 技術棧下,不可變資料結構是大家都提倡的做法,所以又很多框架層面的東西幫我們處理了這些事情。

但是實際上,我們在平時的業務程式碼開發中,就需要有 immutable 的意識,善用 ES6 語法,就可以解決我們大部分的場景。

對我們團隊感興趣的可以關注專欄,關注github或者傳送簡歷至’tao.qit####alibaba-inc.com’.replace(‘####’, ‘@’),歡迎有志之士加入~

原文地址:github.com/ProtoTeam/b…

來源:https://juejin.im/post/5aa8ae316fb9a028bd4c0202?utm_medium=fe&utm_source=weixinqun

相關文章