javascript-avoid-comments-with-refactoring
下面这段代码是今天在Youtube上看到的一个视频中的代码, 代码的功能是从一个字符串数组中找出所有最长的字符串:比如给定字符串数组:[“aba”, “aa”, “ad”, “vcd”, “aba”],则要求返回[“aba”, “vcd”, “aba”]。
可以看到这段代码中有很多注释,我们的目的是优化这段代码,让代码可以self explanation
。
1 | // gets all longest strings |
命名优化
可以从以下几个方面入手:
- 函数名字不够清晰,
longestString
是单数形式,且没有动词,一般函数名字都应该加动词,可以改为getLongestStrings
- 这样我们就知道,这个函数返回的是多个字符串。而且是从输入参数中get
而来的。 - 函数的参数名字不够清晰,
inputArray
是一个数组,但是我们不知道这个数组的内容是什么,而且input
这个单词有点多余,参数当然是input
的,没必要再加上input
这个单词。可以改为stringArray
。
此时,我们的代码变成了这样:
1 | function getLongestStrings(stringArray) { |
- 原来的代码中有一个
length
变量,这个变量的名字不够清晰,并没有说明是什么长度,也无法表明是最大长度,可以改为longestStringLength
。 - 原代码中使用的是经典的
for
循环,在Modern JavaScript中,我们更推荐函数式编程,所以可以改为for...of
循环或者使用Array.prototype.forEach
,这样代码更加简洁。
此时代码变成下面的样子。
1 | function getLongestStrings(stringArray) { |
性能优化
此时代码中有一个forEach
,有一个filter
,这两个函数都是遍历数组的,所以实际上我们遍历了两次数组,不够高效,将其改为一次遍历。此时代码变成下面的样子。
1 | function getLongestStrings(stringArray) { |
使用reduce
优化
多数数组的遍历方法都可以使用reduce来实现,所以我们可以将上面的代码改为使用reduce
来实现。
1 | function getLongestStrings(stringArray) { |
坦率的说,使用reduce
这一版的可读性不如上一版,大家酌情使用。
reduce
通常用来处理比较简单的逻辑,比如累加一个数组中所有的数字。在这种情况下,reduce
通常只接受一个参数,就是待处理的数组。
1 | const sum = [1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0); |
如果逻辑比较复杂,那么reduce就要使用多个参数,比如上面的例子,我们实际上传入了如下两个参数,只不过我们把这两个参数封装到了一个对象中。
- longesStringLength - 用来记录当前最长的字符串的长度
- longestStrings - 用来记录当前最长的字符串
处理的时候数组longestStrings
是不断变化的,如果当前字符串的长度等于longestStringLength
,那么就把当前字符串加入到longestStrings
中。(此过程数组元素不断增多,原有的元素还在),如果当前字符串的长度大于longestStringLength
,那么就把longestStrings
清空,然后把当前字符串加入到longestStrings
中。(此过程原有数组元素被清空,只有当前元素)。
此外,我们还注意到,第一个if分支有return,而第二个没有,有点奇怪,其实原本可以这样写。
1 | if (str.length > acc.maxLength) { |
但是,当str.length > acc.maxLength
时,直接返回一个新对象也可以,这个对象会作为reduce的下一次迭代的第一个参数。
其实再进一步,我们可以在if-else中也返回一个新对象。
1 | if (str.length > acc.maxLength) { |