讓你的 JS 代碼變得更加優雅且可維護

魚頭在開發的過程當中,總結了一些優化開發的編碼經驗,固然這些經驗都是前人總結出來的,此次就特別拿出來跟你們一塊兒分享,固然這些經驗不必定是最佳實踐,各位讀者有興趣或者有不一樣意見的能夠跟魚頭一塊兒探討一下。javascript

拒絕魔法

衆所周知,魔法是這樣的:vue

哦,不是。。java

在編程的世界裏也有魔法,通常稱其爲:魔法數字,魔法變量,魔法字符串。例如這樣:git

const a = await abcdefg();
console.log(a === 200);
const b = await asdfgh();
if (b === 0) {
} else if (b === 1) {
} else if (b === 2) {};
for (let i = 0; i < 10; i++) {};
複製代碼

以上直接出現的,莫名其妙的變量名,字符串以及判斷條件數字,就叫魔法。。。github

這種寫法寫出來的代碼晦澀難懂,難以維護,隱藏 BUG 多,除非你準備給接手的人埋坑,或者準備辭職,否則千萬別這麼寫(容易被打斷腿,👨‍🦽 )算法

那麼怎麼寫才更優雅?編程

語義化

首先即是語義化。一個是變量,常量的語義化,例如:設計模式

const SUCCESS_STATUS = 200;
const requestStatus = await getStatus();
console.log(requestStatus === SUCCESS_STATUS);
const userRole = await getUserRole();
const GUEST_CODE = 0;
const USER_CODE = 1;
const ADMIN_CODE = 2;
if (userRole === GUEST_CODE) {
} else if (userRole === USER_CODE) {
} else if (userRole === ADMIN_CODE) {};
const MAX_NUM = 10;
const MIN_NUM = 0;
for (let currentNum = MIN_NUM; currentNum < MAX_NUM; currentNum++) {};
複製代碼

通常的規則就是變量用小寫,常量用大寫,把變量名語義化,那麼當你看到這段代碼的時候,一眼就能知道它是作什麼的,而不是非得要浪費時間看完上下文,或者是猜。markdown

枚舉

對於上面判斷 userRole 的代碼,其實咱們能夠用更優雅的方式去實現,那就是 枚舉async

按照維基百科的說明:在數學和計算機科學理論中,一個集的枚舉是列出某些有窮序列集的全部成員的程序,或者是一種特定類型對象的計數。這兩種類型常常(但不老是)重疊。

其實就是組織收集有關聯變量的一種方式。枚舉的好處在於方便多狀態的管理,以及可讀性更強。例如:

const ROLES = {
  GUEST: 0,
  USER: 1,
  ADMIN: 2
};
const userRole = await getUserRole();
if (userRole === ROLES.GUEST) {
} else if (userRole === ROLES.USER) {
} else if (userRole === ROLES.ADMIN) {};
複製代碼

經過枚舉的方式概括起來,維護起來更方便,並且要添加狀態直接在 ROLES 對象裏寫就行,更方便快捷。

策略模式

維基百科上說:策略模式做爲一種軟件設計模式,指對象有某個行爲,可是在不一樣的場景中,該行爲有不一樣的實現算法。

上面的代碼依舊是可優化的,在這裏咱們能夠利用策略模式來作進一層的優化。

具體的例子就是以下:

const ROLES = {
  GUEST: 0,
  USER: 1,
  ADMIN: 2
};
const ROLE_METHODS = {
  [ROLES.GUEST]() {},
  [ROLES.USER]() {},
  [ROLES.ADMIN]() {},
};
const userRole = await getUserRole();
ROLE_METHODS[userRole]();
複製代碼

經過上面的寫法,咱們能夠知道,當咱們須要增長角色,或者修改角色數字的時候,只須要修改 ROLES 裏對應的字段,以及 ROLE_METHODS 裏的方法便可,這樣咱們就能夠將可能很冗長的 if...else 代碼給抽離出來,顆粒度更細,更好維護。

更在狀態

除了上面的方式以外,咱們還能夠利用「 狀態 」的概念來寫代碼。在看代碼以前,咱們先了解下什麼是 「有限狀態機」。

根據維基百科的解釋:有限狀態機(英語:finite-state machine,縮寫:FSM)又稱有限狀態自動機(英語:finite-state automation,縮寫:FSA),簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動做等行爲的數學計算模型。

例如咱們熟悉的 Promise ,它就是在狀態集:PENDINFULFILLEDREJECTED 之間單向流轉的有限狀態機。

狀態機的概念跟策略模式相似,實現方式也相似,這裏面最大的不一樣是在於 「語義」 。

策略模式更適合於互不依賴,同時只能存在一個狀態的場景,例如:

const 吃 = {
  沙縣大酒店() {
    吃雲吞()
  },
  開封菜() {
    吃漢堡()
  },
  在家() {
    吃外賣()
  }
};
複製代碼

這裏面若是咱們肚子餓了,就只能在 沙縣大酒店()開封菜()在家() 這幾個狀態裏選。

你不能都吃,固然如下狀況除外。。。

若是是狀態模式,則會有這種狀況:

const 打工人 = {
  起牀() {},
  上班() {},
  加班() {},
  下班() {}
};
// 早上6點
打工人.起牀();
// 早上9點
打工人.上班();
// 晚上6點
打工人.加班();
// 晚上12點
打工人.下班();
複製代碼

這裏的打工人根據不一樣的時間,進行不一樣的任務,即是打工人模式,哦不,狀態模式。這裏的時間就是狀態。

咱們舉個實際的業務例子,就是訂單列表頁,一般咱們的訂單可能有這幾種狀態:

不一樣的狀態展現的 UI 也不一樣,因此咱們以不一樣的狀態劃分好模塊以後,代碼寫起來就會清晰不少,咱們以 Vue 代碼爲例:

// contants.js
export const ORDER_STATUS = {
  INIT: 0, // 初始化
  CREATED: 1, // 訂單建立
  ARREARAGE: 2, // 待支付
  PURCHASED: 3, // 已購買
  SHIPPED: 4, // 已發貨
  COMPLETED: 5 // 已完成
};
複製代碼
// order.vue
<template>
  <div>
        <section v-if="orderIsInit"></section>
        <section v-if="orderIsCreated"></section>
        <section v-if="orderIsArrearage"></section>
        <section v-if="orderIsPurchased"></section>
        <section v-if="orderIsShipped"></section>
        <section v-if="orderIsCompleted"></section>
  </div>
</template>

<script>
  import ORDER_STATUS from './contants';
  import eq from 'lodash';
  
  export default {
    computed: {
      /**
       * @func
       * @name orderIsInit
       * @desc 判斷訂單是否初始化的狀態
       * @returns {string} 判斷訂單是否初始化的狀態
       */
      orderIsInit() {
        return eq(this.orderStatus, ORDER_STATUS.INIT);
      },
      /**
       * @func
       * @name orderIsCreated
       * @desc 判斷訂單是否已建立的狀態
       * @returns {string} 訂單是否已建立
       */
      orderIsCreated() {
        return eq(this.orderStatus, ORDER_STATUS.CREATED);
      },
      /**
       * @func
       * @name orderIsArrearage
       * @desc 判斷訂單是否未付款的狀態
       * @returns {string} 訂單是否未付款
       */
      orderIsArrearage() {
        return eq(this.orderStatus, ORDER_STATUS.ARREARAGE);
      },
      /**
       * @func
       * @name orderIsPurchased
       * @desc 判斷訂單是否已購買的狀態
       * @returns {string} 訂單是否已購買
       */
      orderIsPurchased() {
        return eq(this.orderStatus, ORDER_STATUS.PURCHASED);
      },
      /**
       * @func
       * @name orderIsShipped
       * @desc 判斷訂單是否已發貨的狀態
       * @returns {string} 訂單是否已發貨
       */
      orderIsShipped() {
        return eq(this.orderStatus, ORDER_STATUS.SHIPPED);
      },
      /**
       * @func
       * @name orderIsCompleted
       * @desc 判斷訂單是否已完成的狀態
       * @returns {string} 訂單是否已完成
       */
      orderIsCompleted() {
        return eq(this.orderStatus, ORDER_STATUS.COMPLETED);
      },
    },
    data() {
      return {
        orderStatus: ORDER_STATUS.INIT // 訂單狀態
      }
    },
    methods: {
      /**
       * @func
       * @name getOrderStatus
       * @desc 判斷訂單狀態
       * @returns {string} 返回當前訂單狀態
       */
      async getOrderStatus() {}
    },
    async created() {
      this.orderStatus = await this.getOrderStatus();
    }
  }
</script>
複製代碼

將頁面組件按狀態劃分,實現獨立自治,這樣子既能防止代碼耦合,方便維護 debug,也方便開發者自測,若是須要看不一樣狀態的展現效果,只要手動給 orderStatus 賦值便可,方便快捷。

面向切面

按照維基百科的解釋:面向切面的程序設計(Aspect-oriented programming,AOP,又譯做面向方面的程序設計、剖面導向程序設計)是計算機科學中的一種程序設計思想,旨在將橫切關注點與業務主體進行進一步分離,以提升程序代碼的模塊化程度。

上面這段文字估計沒有什麼人看,算了,直接上代碼吧

咱們看回上面打工人的場景,假定老闆想要知道打工人每一個狀態開始前跟結束前的時間以及作點什麼,那麼該怎麼作呢?這個時候咱們不難想到能夠直接往狀態函數裏寫代碼,例如:

const 打工人 = {
  起牀() {
    老闆.start();
    打工人.do();
    老闆.end();
  },
  上班() {
    老闆.start();
    打工人.do();
    老闆.end();
  },
  加班() {
    老闆.start();
    打工人.do();
    老闆.end();
  },
  下班() {
    老闆.start();
    打工人.do();
    老闆.end();
  }
};
// 早上6點
打工人.起牀();
// 早上9點
打工人.上班();
// 晚上6點
打工人.加班();
// 晚上12點
打工人.下班();
複製代碼

可是這樣打工人一會兒就察覺到到了老闆在監控他的生活,若是要作到不被人察覺(不影響業務邏輯),那咱們既能夠採用 AOP 的實現方式。代碼以下:

import eq from 'lodash';
const TYPES = {
  FUNCTION: 'function'
}
const 老闆監控中的打工人 = new Proxy(打工人, {
    get(target, key, value, receiver) {
        console.log('老闆開始看你了~');
      	const res = Reflect.get(target, key, value, receiver);
      	const 打工人任務 = eq(typeof res, TYPES.FUNCTION) ? res() : res;
        console.log('老闆開始記你小本本了~');
        return () => 打工人任務;
    }
});
複製代碼

因此咱們能夠看到如下結果:

這樣子,咱們就能夠輕鬆簡單地監控到了打工人天天干的活,並且還不讓打工人發現,簡直是資本家聽了都流淚呀。

後記

上面總結的只是諸多編程規範模式其中一小部分,還有許多諸如 S.O.L.I.D 以及其他20幾個設計模式本文沒有說起到,篇幅有限,敬請原諒。

相關文章
相關標籤/搜索