前言
本篇文章主要是经验的浓缩,如果要详细的撰写建议可以参考看看:无瑕的程序代码 JavaScript,里面有更全面的说明与案例。
方法一:良好的命名
刚学习程序的时候太常会一头栽入逻辑、语法或框架中,而忽略最简单且重要的事情:「良好的命名」。在批改了无数种类的作业之后发现这是最常发生也是最 容易 (困难) 被解决的问题。
良好的命名可以真正的帮助提升任何种类代码的质量,帮助你自己与他人更好的了解程序的脉络,它甚至与写程序无关!所以任何人应当都能够理解这个概念。
那么「良好的命名」普遍具备那些条件?
明确且简单
命名应当明确且简白描述该命名事物的内容,举例来说声明一个用户名的变量,内容是什么就描述清楚,不要含糊也不要赘述:
// a 是什么意思? (无意义)const a = 'Joe';
// 暂时的数据? (统称)const usernameTemp = 'Joe';
// 它当然是一笔资料 (赘述)const TheUsername = 'Joe';const usernameData = 'Joe';const usernameValue = 'Joe';const usernameInfo = 'Joe';const usernameContent = 'Joe';
// 什么都没描述清楚 (统称+赘述)const apiUrl = 'www.example.com/api';const dataStore = 'Joe';const str = 'Joe';
// 更 OK 的命名方式const username = 'Joe'; // 用户名称,清楚const attractionUrl = 'www.example.com/api'; // 景点网址,清楚
// 获取……什么资料?function getData() { // ...}
// 获取用户资料,OKfunction getUser() { // ...}
// 获取远端用户资料,OKfunction fetchUser() { // ...}
const locations = ['Austin', 'New York', 'San Francisco'];
// l 是什么意思?locations.forEach((l) => { dispatch(l);});
// 其中的地点,OKlocations.forEach((location) => { dispatch(location);});
简单来说,如果命名只是一些填充用的字,并没有什么实际的意义,就应该在命名中避免!虽然上面的例子看起来理所当然,但说不定在压力与时间的挤压或无意识中真的会写出这样的命名。写小程序还好,但规模只要一扩大就会变成一场灾难,千万不能有“只是在练习,先写再改的侥幸心态”,那么这个诅咒将会一直困扰你与阅读你代码的人 👻。
避免缩写
使用缩写会让程序变得更加难以理解,虽然当下看起来简洁好懂但也应当极力避免。举例来说你正在制作一个游戏的血量机制:Health Point
(HP),这样命名很常见也很合理,但不常打游戏的人可能会清楚这是什么意思,或者之后出现了 Hit Point
? High Point
? Highest Point
? 命名上就出现了冲突。因此除了常见的专有名词像是:HTML
、XML
或 API
还能接受外,能不用缩写就不要使用缩写。
避免神奇数字
神奇数字是指在程序中让人无法理解其用途的数字,举例来说:
function isPasswordValid(password) { if (password.length < 5) { return false; }
if (password.length > 16) { return false; }
return true;}
为了让程序更好被理解、查询与修改,不应该散落“神奇数字”在程序之中,把它们声明为一个变量并且取个好名字通常来说会是更好的做法:
function isPasswordValid(password) { const passwordMinLength = 5; const passwordMaxLength = 16;
if (password.length < passwordMinLength) { return false; }
if (password.length > passwordMaxLength) { return false; }
return true;}
如果有“未来可能改动”或是“单看数字很难理解用途”的情况,就应该把它们声明为一个变量并且给它一个好名称。
避免使用 var
var
是一个早期变量声明方式,简单来说它会让你的变量变成全局变量,这样会让程序变得难以维护,因此应当避免使用 var
。事实是:你只会越来越少见 var
,请视情况使用 const
或 let
。
使用命名惯例
不同的程序语言社区有不同的命名惯例,常见你会看到:
- CamelCase (大驼峰)
- camelCase (小驼峰)
- kebab-case (烤肉串)
- snake_case (蛇型)
等等很多其他类型的缩写……这些命名惯例都是为了让程序更一致而存在的规范,举例来说:JavaScript 通常会使用 camelCase
,而 CSS 或 HTML 通常会使用 kebab-case
。
就算没有参与多人开发的环境,也迟早要阅读他人的代码,所以尽早养成该语言普遍在用的命名惯例是越早越好。
除了会用之外还要一致,如果一个程序里面包含了多种撰写规范会导致难以维护,是应当极力避免的。
方法二:避免注释
如果依循严格的命名与规范,程序代码通常就足够清楚到不需要注释了,过多的注释绝对会影响到程序的可读性! 对我来说注释适合用来描述更高层级的概念,例如:为什么要这样架构程序?而不是程序实现上的细节描述。甚至注释是不需要的,因为更高层级的概念可以被在文档当中描述。
这个部分比较倾向偏好了,我相信好的程序代码就是最好的注释,如果你也觉得这样比较好,那么就尽量避免使用注释吧!
方法三:不要重复造轮子
每当在写程序时,我们无时无刻都可以站在他人的肩膀上,只要你懂得怎么运用,使用现有的解决方案是最节省时间精力的事,也可以让代码更简洁易于维护。常见的问题通常早就已经有他人深思熟虑过的最优解,像是操纵数据强烈建议可以补齐 操纵数组的方法 。 (这里预计会再写一篇专门的文章讲解 😉)
除了把重点真正放在如何解决问题而不是如何实现之外,也可以让你的代码更容易被其他人理解。
方法三:使用最合适的资料型态
在 JavaScript 中,有很多资料型态可以使用,像是 string
、number
、boolean
、array
、object
、null
、undefined
等等,每个资料型态都有自己的特性,使用最合适的资料型态可以让代码更简洁易读,也可以避免一些不必要的错误。
举例来说使用 string
来储存一个数字,可能会导致一些不必要的错误,例如:'1' + '1'
会变成 '11'
而不是 2
,这样的错误很难被发现;或是使用 string
来储存一个开关的状态,这样的代码会让人很难理解,因为 string
通常会让人联想到储存字符串而不是是或否的结果。
挑选正确的资料型态可以让代码更简洁易读,也可以避免一些不必要的错误。
方法四:去除冗余的逻辑
举一个计算是否输入的值为奇数的程序,与其判断结果并返回 true
或 false
,直接返回结果就好了:
// ❌ 冗余的逻辑function isOdd(number) { if (number % 2 === 1) { return true; } else { return false; }}
// ✅ OKconst isOdd = (num) => { return num % 2 === 1;};
有的冗余逻辑则是因为创造“自己才明白的抽象”才导致的,可以参考看看以下示例,其中将“被选取”的项目用“自定义状态码”来呈现,这样的抽象做只会单纯的损害代码的可读性,并没有任何实质的帮助 😅:
// ❌ 冗余的逻辑const selected = 0;
if (selected === 0) { showVegetable();} else if (selected === 1) { showFruit();} else if (selected === 2) { showFlower();}
// ✅ OKconst selected = 'flower';
if (selected === 'vegetable') { showVegetable();} else if (selected === 'fruit') { showFruit();} else if (selected === 'flower') { showFlower();}
甚至与其去判断,不如把对象作为一个查找表,这样的写法会更简洁容易更改,易读的话就看人了:
// 😎 OKconst productTable = { vegetable: showVegetable, fruit: showFruit, flower: showFlower,};
if (isSelected) { productTable[selected]();}
function showVegetable() {}function showFruit() {}function showFlower() {}
或是你也可以把三个 show某某
的函数用一个 show
函数来取代并传入参数,不过还是要看代码前后脉络,这里就不深入讨论了。