最近在油管看了一个视频,名字叫 Junior Vs Senior Code - How To Write Better Code,可以说标题立刻勾起了我的好奇心,我真的很好奇一个初级程序员和一个高级程序员在面对同样业务逻辑的时候,写出的代码为什么会天差地别。然而看完该视频后,我对于作者对高级程序员的看法有些不同的见解。
就 JavaScript 而言,在作者的观点中,一个 pro 应该:
- 所有的代码都必须尽可能的简洁干净
- 考虑所有可能出现的错误,并在方法中处理掉
然而事实上,我们真的需要这样写代码吗?
先来看这样一段代码:
[].forEach.call($$('*'), function(a) {a.style.outline = "1px solid #" + (~~(Math.random()*(1<<24))).toString(16)})
这是 Addy Osmani 在 Github Gist 上面发布一段用来调试 CSS 的代码,如果你把它复制进浏览器的 Devtools 控制台并按下回车执行它,它会给每一个元素都加上不同颜色的 outline。抛开代码中用到的技术不谈,单论这样一行代码,你觉得它专业吗?我的答案是:
It depends.
如果你是参加一个编程比赛,主题是用最少的代码让浏览器上每个元素都显示出不同颜色的 outline,那这样一行代码可以说是非常专业了,很可能你就是冠军了。但是如果这是一个团队协作的项目,你给出了这样的代码,code reviewer 反而可能会骂死你。为什么?因为你的代码可读性几乎为零,除了炫技,一无是处。作为团队中的一员,保证其他成员能以最低成本看懂你的代码才应该是最高优先级的。
/**
* @description put outline on every single element in the document
*/
function outlineEverything() {
let everyElements = document.querySelectorAll('*')
everyElements.forEach((el) => {
let randomColor = makeRandomColor()
el.style.outline = `1px solid #${randomColor}`
})
}
/**
* @description get a random hex color value `rrggbb` without the `#`
* @returns {string}
*/
function makeRandomColor() {
// get a random number between 0 - (256 * 256 * 256)
let _r = parseInt(Math.random() * (256 * 256 * 256))
// return number in hexadecimal
return _r.toString(16)
}
outlineEverything()
再来看看现在的代码,在保证同样功能性的前提下,大幅提高了代码的可读性,这才是我们在开发阶段需要的代码啊!
可能有人会说性能问题,那如果我告诉你后者的性能只比前者差不到 5% 你会怎么想?
这 5% 的性能损失可能会换回同事的不杀之恩也不一定呢?而且很多时候,并不是代码少,程序就能跑得快的,关于这一点以后可以专门写一篇博客来说明一下。
—— 在开发阶段,你越是压缩代码的写法,在维护阶段它越有可能被删掉。
下面再来说说,自己的代码中究竟应不应该处理所有可能出现的错误。来看看这样一段代码:
let itemData = {
price: 10,
quantity: 3,
discount: 0.05 // rate in 0-1 scale
}
/**
* @description get items total amount of money with discount applied
* @param {object} item
* @param {number} item.price
* @param {number} item.discount
* @param {number} item.quantity
* @returns {string} total money
*/
function getTotal(item) {
let _total = item.quantity * item.price
let _discountAppliedTotal = _total * (1 - item.discount)
return _discountAppliedTotal.toFixed(2)
}
console.log(getTotal(itemData))
如果执行正确的话,那么控制台应该输出28.50
,因为一共 3 件商品,商品单价为 10,再加上 5% 的折扣,那总价确实是 28.50,按照现实中的逻辑,这样的代码不管是从可读性和健壮性来说其实已经非常好了。但是以视频作者的意思,任何可能出错的地方都要加以处理,也就是说,我们需要考虑商品数量、折扣和单价都有可能会出现null
或者undefined
值,如此缜密的心思当然是值得鼓励的,但是从实际开发角度看去这会让原本 clean 的代码变得 not that clean:
function getTotal(item) {
if(item.discount === undefined || item.discount === null){
return 0
}
if(item.price === undefined || item.price === null || item.price === 0){
return 0
}
if(item.quantity === undefined || item.quantity === null || item.quantity === 0){
return 0
}
let _total = item.quantity * item.price
let _discountAppliedTotal = _total * (1 - item.discount)
return _discountAppliedTotal.toFixed(2)
}
getTotal
方法的代码瞬间比之前多了一倍,而且这么写下来真的有意义吗?如果后端真的返回给你的数值是负数或者为null
,这难道不应该是后端的问题吗?为什么要在前端进行静默处理?是觉得 debug 难度还不够大吗?
—— 任何代码的出现都应当以实际业务逻辑为参考系,否则毫无意义。
总结:脚踏实地,事无巨细,这样写出来的代码,才是专业的代码。