文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

让你的 JS 代码变得更加优雅且可维护

2024-12-03 13:34

关注

拒绝魔法

众所周知,魔法是这样的:

[[375838]]

哦,不是。。

在编程的世界里也有魔法,一般称其为:魔法数字,魔法变量,魔法字符串。例如这样:

  1. const a = await abcdefg(); 
  2. console.log(a === 200); 
  3. const b = await asdfgh(); 
  4. if (b === 0) { 
  5. } else if (b === 1) { 
  6. } else if (b === 2) {}; 
  7. for (let i = 0; i < 10; i++) {}; 

以上直接出现的,莫名其妙的变量名,字符串以及判断条件数字,就叫魔法。。。

这种写法写出来的代码晦涩难懂,难以维护,隐藏 BUG 多,除非你准备给接手的人埋坑,或者准备辞职,不然千万别这么写(容易被打断腿,👨‍🦽 )

那么怎么写才更优雅?

语义化

首先便是语义化。一个是变量,常量的语义化,例如:

  1. const SUCCESS_STATUS = 200
  2. const requestStatus = await getStatus(); 
  3. console.log(requestStatus === SUCCESS_STATUS); 
  4. const userRole = await getUserRole(); 
  5. const GUEST_CODE = 0
  6. const USER_CODE = 1
  7. const ADMIN_CODE = 2
  8. if (userRole === GUEST_CODE) { 
  9. } else if (userRole === USER_CODE) { 
  10. } else if (userRole === ADMIN_CODE) {}; 
  11. const MAX_NUM = 10
  12. const MIN_NUM = 0
  13. for (let currentNum = MIN_NUM; currentNum < MAX_NUM; currentNum++) {}; 

一般的规则就是变量用小写,常量用大写,把变量名语义化,那么当你看到这段代码的时候,一眼就能知道它是做什么的,而不是非得要浪费时间看完上下文,或者是猜。

枚举

对于上面判断 userRole 的代码,其实我们可以用更优雅的方式去实现,那就是 枚举 。

按照维基百科的说明:在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。

其实就是组织收集有关联变量的一种方式。枚举的好处在于方便多状态的管理,以及可读性更强。例如:

  1. const ROLES = { 
  2.   GUEST: 0, 
  3.   USER: 1, 
  4.   ADMIN: 2 
  5. }; 
  6. const userRole = await getUserRole(); 
  7. if (userRole === ROLES.GUEST) { 
  8. } else if (userRole === ROLES.USER) { 
  9. } else if (userRole === ROLES.ADMIN) {}; 

通过枚举的方式归纳起来,维护起来更方便,而且要添加状态直接在 ROLES 对象里写就行,更方便快捷。

策略模式

维基百科上说:策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

上面的代码依旧是可优化的,在这里我们可以利用策略模式来做进一层的优化。

具体的例子就是如下:

  1. const ROLES = { 
  2.   GUEST: 0, 
  3.   USER: 1, 
  4.   ADMIN: 2 
  5. }; 
  6. const ROLE_METHODS = { 
  7.   [ROLES.GUEST]() {}, 
  8.   [ROLES.USER]() {}, 
  9.   [ROLES.ADMIN]() {}, 
  10. }; 
  11. const userRole = await getUserRole(); 
  12. ROLE_METHODS[userRole](); 

通过上面的写法,我们可以知道,当我们需要增加角色,或者修改角色数字的时候,只需要修改 ROLES 里对应的字段,以及 ROLE_METHODS 里的方法即可,这样我们就可以将可能很冗长的 if...else 代码给抽离出来,颗粒度更细,更好维护。

更在状态

除了上面的方式之外,我们还可以利用“ 状态 ”的概念来写代码。在看代码之前,我们先了解下什么是 “有限状态机”。

根据维基百科的解释:有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automation,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。

例如我们熟悉的 Promise ,它就是在状态集:PENDIN 、 FULFILLED 、 REJECTED 之间单向流转的有限状态机。

状态机的概念跟策略模式类似,实现方式也类似,这里面最大的不同是在于 “语义” 。

策略模式更适合于互不依赖,同时只能存在一个状态的场景,例如:

  1. const 吃 = { 
  2.   沙县大酒店() { 
  3.     吃云吞() 
  4.   }, 
  5.   开封菜() { 
  6.     吃汉堡() 
  7.   }, 
  8.   在家() { 
  9.     吃外卖() 
  10.   } 
  11. }; 

这里面如果我们肚子饿了,就只能在 沙县大酒店() , 开封菜() , 在家() 这几个状态里选。

你不能都吃,当然以下情况除外。。。

如果是状态模式,则会有这种情况:

  1. const 打工人 = { 
  2.   起床() {}, 
  3.   上班() {}, 
  4.   加班() {}, 
  5.   下班() {} 
  6. }; 
  7. // 早上6点 
  8. 打工人.起床(); 
  9. // 早上9点 
  10. 打工人.上班(); 
  11. // 晚上6点 
  12. 打工人.加班(); 
  13. // 晚上12点 
  14. 打工人.下班(); 

这里的打工人根据不同的时间,进行不同的任务,便是打工人模式,哦不,状态模式。这里的时间就是状态。

我们举个实际的业务例子,就是订单列表页,通常我们的订单可能有这几种状态:

不同的状态展示的 UI 也不同,所以我们以不同的状态划分好模块之后,代码写起来就会清晰很多,我们以 Vue 代码为例:

  1. // contants.js 
  2. export const ORDER_STATUS = { 
  3.   INIT: 0, // 初始化 
  4.   CREATED: 1, // 订单创建 
  5.   ARREARAGE: 2, // 待支付 
  6.   PURCHASED: 3, // 已购买 
  7.   SHIPPED: 4, // 已发货 
  8.   COMPLETED: 5 // 已完成 
  9. }; 
  1. // order.vue 
  2. <template> 
  3.   <div> 
  4.         <section v-if="orderIsInit">section> 
  5.         <section v-if="orderIsCreated">section> 
  6.         <section v-if="orderIsArrearage">section> 
  7.         <section v-if="orderIsPurchased">section> 
  8.         <section v-if="orderIsShipped">section> 
  9.         <section v-if="orderIsCompleted">section> 
  10.   div> 
  11. template> 
  12.  
  13. <script> 
  14.   import ORDER_STATUS from './contants'; 
  15.   import eq from 'lodash'; 
  16.    
  17.   export default { 
  18.     computed: { 
  19.        
  20.       orderIsInit() { 
  21.         return eq(this.orderStatus, ORDER_STATUS.INIT); 
  22.       }, 
  23.        
  24.       orderIsCreated() { 
  25.         return eq(this.orderStatus, ORDER_STATUS.CREATED); 
  26.       }, 
  27.        
  28.       orderIsArrearage() { 
  29.         return eq(this.orderStatus, ORDER_STATUS.ARREARAGE); 
  30.       }, 
  31.        
  32.       orderIsPurchased() { 
  33.         return eq(this.orderStatus, ORDER_STATUS.PURCHASED); 
  34.       }, 
  35.        
  36.       orderIsShipped() { 
  37.         return eq(this.orderStatus, ORDER_STATUS.SHIPPED); 
  38.       }, 
  39.        
  40.       orderIsCompleted() { 
  41.         return eq(this.orderStatus, ORDER_STATUS.COMPLETED); 
  42.       }, 
  43.     }, 
  44.     data() { 
  45.       return { 
  46.         orderStatus: ORDER_STATUS.INIT // 订单状态 
  47.       } 
  48.     }, 
  49.     methods: { 
  50.        
  51.       async getOrderStatus() {} 
  52.     }, 
  53.     async created() { 
  54.       this.orderStatus = await this.getOrderStatus(); 
  55.     } 
  56.   } 
  57. script> 

将页面组件按状态划分,实现独立自治,这样子既能防止代码耦合,方便维护 debug,也方便开发者自测,如果需要看不同状态的展示效果,只要手动给 orderStatus 赋值即可,方便快捷。

面向切面

按照维基百科的解释:面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计、剖面导向程序设计)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。

上面这段文字估计没有什么人看,算了,直接上代码吧

[[375840]]

我们看回上面打工人的场景,假定老板想要知道打工人每个状态开始前跟结束前的时间以及做点什么,那么该怎么做呢?这个时候我们不难想到可以直接往状态函数里写代码,例如:

  1. const 打工人 = { 
  2.   起床() { 
  3.     老板.start(); 
  4.     打工人.do(); 
  5.     老板.end(); 
  6.   }, 
  7.   上班() { 
  8.     老板.start(); 
  9.     打工人.do(); 
  10.     老板.end(); 
  11.   }, 
  12.   加班() { 
  13.     老板.start(); 
  14.     打工人.do(); 
  15.     老板.end(); 
  16.   }, 
  17.   下班() { 
  18.     老板.start(); 
  19.     打工人.do(); 
  20.     老板.end(); 
  21.   } 
  22. }; 
  23. // 早上6点 
  24. 打工人.起床(); 
  25. // 早上9点 
  26. 打工人.上班(); 
  27. // 晚上6点 
  28. 打工人.加班(); 
  29. // 晚上12点 
  30. 打工人.下班(); 

但是这样打工人一下子就察觉到到了老板在监控他的生活,如果要做到不被人察觉(不影响业务逻辑),那我们既可以采用 AOP 的实现方式。代码如下:

  1. import eq from 'lodash'; 
  2. const TYPES = { 
  3.   FUNCTION: 'function' 
  4. const 老板监控中的打工人 = new Proxy(打工人, { 
  5.     get(target, key, value, receiver) { 
  6.         console.log('老板开始看你了~'); 
  7.        const res = Reflect.get(target, key, value, receiver); 
  8.        const 打工人任务 = eq(typeof res, TYPES.FUNCTION) ? res() : res; 
  9.         console.log('老板开始记你小本本了~'); 
  10.         return () => 打工人任务; 
  11.     } 
  12. }); 

所以我们可以看到以下结果:

这样子,我们就可以轻松简单地监控到了打工人每天干的活,而且还不让打工人发现,简直是资本家听了都流泪呀。

 

来源:鱼头的Web海洋内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯