让你的 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几个设计模式本文没有说起到,篇幅有限,敬请原谅。

相关文章
相关标签/搜索