咱們須要注意的 immutable 操做

做者簡介:hustcc 螞蟻金服·數據體驗技術團隊javascript

immutable 是什麼?不變的、一成不變的。在 Javascript 中通常指一個變量在通過一個 function 處理以後,能夠保持入參數據不變。java

1、什麼是 immutable?

舉個真實平常例子:前兩天,在業務代碼中,須要得到數據的中位數,並使用 echarts 繪製出中位線,輔助分析。寫代碼以前,先找了一下中位線的定義。react

對於有限的數集,能夠經過把全部觀察值高低排序後找出正中間的一個做爲中位數。若是觀察值有偶數個,一般取最中間的兩個數值的平均值做爲中位數。git

哦,挺簡單,因而奮筆疾書(實際上是 github 上的代碼片斷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 的:npm

當通過函數 calculateMedian 求取數組 [5, 4, 3, 2, 1] 的中位數的時候,致使入參數組變成了 [1, 2, 3, 4, 5]。致使後續的代碼拿到的數組,都是通過排序的,數據都是混亂的。redux

上述代碼只須要修改一個地方便可:數組

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

其中 [].concat(arr) 達到的效果就是代碼的 immutable。數據結構

經過這樣的一個實際例子,我想應該很容易理解 mutable 代碼形成的影響是什麼?架構

  • 數據污染:通過一個方法,數據就變化了,多麼可怕。
  • 數據不可控:數據不只僅是被修改,並且咱們無法控制數據被修改的規律。
  • debug 黑洞:通常的 bug,若是瞭解業務,可能不用 debugger 就能定位一二;mutable 產生的 bug,沒辦法,新增代碼一句句 F10 吧。

2、常見的緣由

不少狀況下,道理咱們都懂,可是仍是會不經意寫出 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 一下,抓住代碼實現的關鍵點。

3、如何寫 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 寫法建立 userB
const userB = {
  ...userA,
  id: 2,
  name: 'ProtoTeam',
};
複製代碼
  • 數組 immutable 操做
const arr = [1, 2, 3, 4, 5];

// push 操做
const arrPush = [
  ...arr,
  6,
];

// shift 操做
const [e, ...arrShift] = arr;

// concat

const arrConcat = [...arr, ...arr];
複製代碼

4、最後

在 react + redux 技術棧下,不可變數據結構是你們都提倡的作法,因此又不少框架層面的東西幫咱們處理了這些事情。

可是實際上,咱們在平時的業務代碼開發中,就須要有 immutable 的意識,善用 ES6 語法,就能夠解決咱們大部分的場景。

對咱們團隊感興趣的能夠關注專欄,關注github或者發送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~

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

相關文章
相關標籤/搜索