[date: 2018-04-15 21:26] [visits: 19]

聊聊Lodash

如果说要推荐工具库给JavaScript新手,第一个我会选择Lodash,为什么推荐这个看似可有可无的工具库呢?因为我觉得Lodash可以传达一种编程理念,通过高层抽象概念使写出来的代码更加优雅简洁。

高层抽象概念

请读者先阅读下面一小段代码,留意自己理解代码所花费的时间,代码如下:

let numbers = [1, 2, 3, 4, 5, 6];
let targets = [];

for(var i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        targets.push(numbers[i]);
    }
}

理解上面这段代码的含义,对所有程序员都不是问题,即从一个数组中挑出所有偶数,具体过程大致如下:

不同读者理解代码花费的时间有所差异,可能是5秒、10秒不等,但不管每个人是多长时间,问大家一个问题:“这段代码在可读性方面是否还有优化空间?”。

代码可读性之所以重要,是因为重要代码被阅读的时间远大于编写所花费的时间,可读性越好,维护成本也就更低,出错概率相应更小。回到上面的问题:“如何优化?”,我的答案是使用filter方法:

const _ = require('lodash');

let numbers = [1, 2, 3, 4, 5, 6];
let targets = _.filter(numbers, item => item % 2 === 0);

// Array.prototype.filter可以达到同样效果

如果对filter不熟悉,理解这段代码可能花费比之前代码更长的时间,但这是因为其中包含学习filter所花费的时间。如果熟悉filter概念,这段代码理解起来一定比前者所花费时间更短。

理解后者比前者更快的原因在于后者的程序语句包含更高层级的抽象概念,而这个概念在你阅读代码前就知道,所以你能更快理解这一小段代码的具体含义。而如果代码未出现filter概念,每次我们都需要从更基础的概念中总结出一个filter的概念,这个过程随着次数增加会变得越发熟练,但这个过程永远存在。(想象一下出现10的地方都写成5 + 5)。

Lodash

介绍Lodash,还是从代码开始:

let list = [{name: 'x', age: 18}, {name: 'y', age: 19}...];

// begin
let res = {};
for(var i = 0; i < list.length - 1; i++) {
    if (!res[list[i].age]) {
        res[list[i].age] = [];
    }

    res[list[i].age].push(list[i]);
}
// end

// begin
let names = [];
for(var i = 0; i < list.length - 1; i++) {
    if (names.indexOf(list[i].name) === -1) {
        names.push(list[i].name);
    }
}
// end

两个begin-end注释之间的代码分别做了什么?是不是比最开始的例子更难理解?第一部分是将list按age分组,获得了一个age为key的对象,每个key对应的值都是同年龄人数组,第二部分则是将list中所有不重复的name挑出来作为一个数组。

使用Lodash改写上述代码:

const _ = require('lodash');
let list = [{name: 'x', age: 18}, {name: 'y', age: 19}...];


let res = _.groupBy(list, 'age');

let names = _.uniq(_.map(list, 'name'));

改写后代码变得更简洁,但最重要的是代码自身传达了更高层面的抽象概念,让我们可以更快理解代码,简洁只是附带优点。

Lodash中的大部分方法都是根据经验将常见代码逻辑总结为一个抽象概念供大家使用,学习并理解其所传达的概念是没有语言边界的,意思是你学到的大部分方法、概念在其他语言中应当都有相对应的替代物。Lodash中的常用方法很多,在此不做过多讲述,大家从文档中学习吧。

如果你在写代码的过程中总感觉有些逻辑重复又重复,也许是时候试试Lodash了。

常用方法:

_.each、_.map、_.pick、_.orderBy、_.groupBy、_.isEmpty、_.filter、_.find、_.padStart、_.omit、_.assign...