程式碼最佳化記錄

發表於2024-02-28

對find的結果進行判空

const items = [
  {
    id: 1,
    name: 'Ben',
    age: 25,
  },
  {
    id: 2,
    name: 'Lily',
    age: 20,
  },
];

const targetItem = items.find((item) => item.age === 50);
console.log(targetItem.name);
// Error: Cannot read properties of undefined (reading 'name')

上面的程式碼中,我們在find的返回值上直接訪問屬性,會有可能導致異常。

因為在沒有找到目標時,find的返回值會是undefined,在undefined上訪問物件,就會丟擲異常。

所以,遵循簡單的策略,永遠對find的返回值進行判空,避免意外情況。

const targetItem = items.find((item) => item.age === 50);
console.log(targetItem?.name);
// undefined

利用map與filter組合過濾

目標:
剔除users不存在於permissions中的使用者資料,並且將permissionscodes寫入users

// 使用者列表資料
const users = [
  {
    id: 1,
    name: 'Ben',
    age: 25,
    codes: [],
  },
  {
    id: 2,
    name: 'Lily',
    age: 20,
    codes: [],
  },
];

// 使用者的許可權code資料
const permissions = [
  {
    id: 1,
    codes: ['create', 'delete'],
  },
  {
    id: 3,
    codes: ['create', 'delete'],
  },
];

先給出一個符合直覺的版本

方案1:

  1. 遍歷users,檢查每個user是否存在於permissions,得到hasPermissonUsers
  2. 對上面的hasPermissonUsers進行遍歷,從permissions中取出每個usercodes
const adminUsers = users
  .filter((user) => permissions.find((permission) => permission.id === user.id))
  .map((user) => ({
    ...user,
    codes: permissions.find((permission) => permission.id === user.id)?.codes ?? [],
  }));

上面的程式碼很好的完成了任務,但是還有一點最佳化的空間。
在上面的程式碼中,filtermap內部都對permissions進行了一次遍歷,有沒有辦法避免呢?

方案2:

  1. 遍歷users,獲取每個user對應的permission,將獲取到的permission.codes存到當前user中,將無匹配結果的資料作為null返回
  2. users進行一次真假值檢查,即可過濾掉不存在於permissions的使用者
const betterAdminUsers = users
  .map((user) => {
    // 用target來實現2個目的
    const target = permissions.find((permission) => permission.id === user.id);

    // 1 獲取permission中的codes
    if (target) {
      return {
        ...user,
        codes: target.codes,
      };
    }
    // 2 用於給後面的filter過濾掉不存在於permissions的使用者
    return null;
  })
  .filter(Boolean);

方案2中,我們減少了一次find查詢,而且程式碼變得更簡單了。

關鍵點就是,在map內部,將本次對permissions的查詢結果透過某種方式傳遞給了filter,避免了filter內部再次permissions對進行查詢。

針對方案2的更新:
這種在map裡面幹兩件事的程式碼,違反了單一職責,感覺沒有必要,增加了閱讀者的心智負擔。

這裡對方案1進行了一點小改造,這樣map裡面保證了單一職責,它只做了資料轉存這一件事。然後filter也只做了過濾這一件事。

const adminUsers = users
  .map((user) => {
    const target = permissions.find((permission) => permission.id === user.id);
    return {
      ...user,
      isHasPermission: !!target,
      codes: target?.codes ?? [],
    };
  })
  .filter((user) => user.isHasPermission);

相關文章