Lodash函数以及常见用法

1 简介

Lodash是一款优秀的 JavaScript 工具库,里面包含了大量的工具函数。在 2015 年就成为被依赖最多的 JavaScript 库,写这篇文档是最新版是 4.17.4,适用于常见浏览器以及 Node.js 等。

这里给出其英文文档中文文档的链接。其中中文文档的版本较低,但是区别不大,可以参考帮助理解。

2 常见用法

在这部分我们介绍一些 Lodash 的常见的优雅的用法。主要是引起大家的学习兴趣,有更多优雅用法等待大家去发现。

_.get获取一个嵌套很深的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
//config 初始化为 null,需要从服务器端获取权限数据
let config = null;
ajax.get(url, function(data) {
// data = {basic: {delete: true}}
config = data;
});
//使用原生JS获取是否有删除权限
let isDeletable = false;
if (config && config.basic) {
isDeletable = config.basic.delete || false;
}
//使用lodash获取是否有删除权限
let isDeletable = _.get(config, 'basic.delete', false);

对应地,可以通过_.set({}, 'a.b.c', 1)创建一个多级嵌套的对象。

_.map获取数组中每个对象的特定字段,并形成一个新的数组

1
2
3
4
5
6
7
8
//使用所有用户的 idCard 字段创建出一个数组
let users = [{ idCard: '20160512', name: '张三' }, { idCard: '20160513', name: '李四' }];
//使用原生JS
let idCards = users.map(function(user) {
return user.idCard;
});
//使用lodash
let idCards = _.map(model, 'idCard');

_.pick取出对象的部分字段形成一个新对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//在修改密码表单里,用户输入了如下字段并被封装到一个对象中
let form = {
password: '123456', //密码
repeatPassword: '123456', //重复密码
code: '5489', //验证码
};

//这里不直接使用delete删除字段,是因为该 form 对象与 DOM 进行了绑定。
//其中重复密码只用来在客户端校验,不需要发送给服务器
let data = {};
let fields = ['password', 'code'];
for (var i = 0; i < fields.length; i++) {
data[fields[i]] = form[fields[i]];
}

//使用lodash
let data = _.pick(form, 'password', 'code');

对应地,还有_.omit方法表示删除部分字段形成一个新对象。

_.random获取一个随机值

1
2
3
4
5
6
7
//获取[90, 100)之内的一个随机值
let min = 90;
let max = 100;
//使用原生JS
let random = Math.floor(Math.random() * (max - min)) + min;
//使用lodash
let random = _.random(min, max);

类似地,_.sample(['a', 'b', 'c'])可以从数组中随机取出一个项目。

_.clamp将一个数字修改成区间中的一个值

1
2
3
4
5
6
7
8
//使用原生JS
function applyRange(number) {
number = Math.max(number, this.props.min);
number = Math.min(number, this.props.max);
return number;
}
//使用lodash
let number = _.clamp(number, min, max);

_.once确保一个函数只会执行一次

1
2
3
4
5
6
7
8
9
10
//使用原生JS
let inited = false;
function init() {
if (inited) return;
// init code
}
//使用lodash
let init = _.once(function() {
// init code
});

_.chain链式操作

链接调用让代码更加整洁;避免了中间变量,避免了错误引用,让代码质量更有保证;_.chain还提供了延迟计算特性,在显式或隐式调用value()方法之前是不进行任何计算的,通过合并大大降低迭代次数。
下面是lodash的官方文档中的一个例子。

1
2
3
4
5
6
7
8
9
10
var users = [{ user: 'barney', age: 36 }, { user: 'fred', age: 40 }, { user: 'pebbles', age: 1 }];

var youngest = _.chain(users)
.sortBy('age')
.map(function(chr) {
return chr.user + ' is ' + chr.age;
})
.first()
.value();
// → 'pebbles is 1'

3 模块

Lodash 的工具函数很多,可以分为以下几类:数组(Array),集合(Collection),函数(Function),Lang(Lang),数学(Math),数字(Number),对象(Object),字符串(String),未分类工具函数(Util)。下面将会按类别介绍常见工具函数。

4 数组

获取子数组

函数名 简介
slice 获取元素第 m-n(不包含)个元素
tail 获取出第一个元素之外的其他元素
initial 获取出最后一个元素之外的其他元素
take 从左侧开始获取任意数量的元素
takeRight 从右侧开始获取任意数量的元素
takeWhile 从左侧开始获取任意数量的元素,直到断言返回假值
takeRightWhile 从右侧开始获取任意数量的元素,直到断言返回假值
drop 丢掉前面几个元素,得到剩余元素
dropWhile 丢掉前面几个元素知道迭代器返回假值,得到剩余元素
dropRight 丢掉后面几个元素,得到剩余元素
dropRightWhile 丢掉后面几个元素知道迭代器返回假值,得到剩余元素

数组常见操作

操作 不修改原数组 修改原数组
移除 without pull
相减 difference pullAll
相减 differenceBy pullAllBy
相减 differenceWith pullAllWith
反转 reverse
裁剪 at pullAt
过滤 filter remove

数组常见操作变种函数 by, with

有些函数还可以稍微变化一下,接受不同的参数,提供更多灵活性。

作用 函数名 by with
相减 difference differenceBy differenceWith
交集 intersection intersectionBy intersectionWith
并集 union unionBy unionWith
异或 xor xorBy xorWith
相减 pullAll pullAllBy pullAllWith,跟difference不同的是,pullAll修改原数组
去重 uniq uniqBy uniqWith
去重 sortedUniq sortedUniqBy

获取数组某个位置上的元素

函数名 主要参数-返回值 简介
head 数组=>元素 返回数组的第一个元素,和first相同
last 数组=>元素 返回数组的最后一个元素,和head相反
nth 数组=>元素 返回数组中某个位置上的元素

检测元素在数组中的索引

函数名 简介
indexOf 获取元素在数组中的索引
sortedIndexOf indexOf功能一致,只是通过二分搜索方法
lastIndexOf 获取元素在数组中的索引,最后一次出现
sortedLastIndexOf lastIndexOf功能一致,只是通过二分搜索方法
findIndex 寻找元素位置
findLastIndex 寻找元素位置,从后往前

检测元素在插在有序数组的什么位置

函数名 简介
sortedIndex 通过二分搜索判断元素应该插在数组的哪个位置
sortedIndexBy 同上,可以额外提供一个迭代器函数
sortedLastIndex sortedIndex类似,但是从右边开始
sortedLastIndexBy 同上,可以额外提供一个迭代器函数

将数组拍平

函数名 主要参数-返回值 简介
flatten 高维数组=>低维数组 将数组拍平
flattenDeep 高维数组=>数组 将数组拍平
flattenDepth 高维数组=>低维数组 将数组拍平

Zip

函数名 主要参数-返回值 简介
zip 多个数组=>二维数组 可以理解为二维数组的行列互换
zipWith 多个数组=>数组 同上,但是可以自由处理行列互换后的数组中的每个数组元素
zipObject 两个数组=>对象 把 keys 和 values 数组组成一个新对象
zipObjectDeep 两个数组=>对象 同上,递归地处理属性名

未分类函数

函数名 主要参数-返回值 简介
chunk 数组=>二维数组 分段形成二维数组
compact 数组=>数组 移除假值
concat 多个数组=>数组 连接多个数组形成一个数组
fill 数组=>数组 填充数组
fromPairs 二维数组=>对象 将键值数组变成对象。和toPairs相反
join 数组=>字符串 拼接数组元素成一个字符串

5 集合

为什么区分集合函数和数组函数?
集合函数不单单适用于数组,还适用于字符串,对象,类数组对象(比如 Arguments,NodeList 等)。字符串是字符的集合,对象是属性值的集合。类数组对象是通过“鸭子类型”工作的,所以如果你传入一个拥有length字段并且值为数字的对象,这个对象会被当做一个数组处理。具体请参考Underscore.js文档。

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function printKeyVal(val, key) {
console.log(key, val);
}

//普通对象
_.each({ a: 1 }, printKeyVal);
//打印结果
// a 1

//拥有值为数字的length字段
_.each({ a: 1, length: 2 }, printKeyVal);
//打印结果
// 0 undefined
// 1 undefined

下面将分类介绍集合相关函数。

遍历

函数名 简介
each forEach
eachRight forEachRight

排序

函数名 简介
sortBy 排序
orderBy sortBy,还可以指定正序倒序
shuffle 返回一个打乱顺序的新数组

过滤

函数名 简介
filter 创建一个新数组,包含了所有让断言为真的元素
reject 创建一个新数组,包含了所有让断言为假的元素
partition 根据断言真假将一个集合分成两个集合

Map 之后再 flatten

函数名 简介
flatMap map之后再flatten
flatMapDeep map之后再flattenDeep
flatMapDepth map之后再flattenDepth

寻找元素

函数名 简介
find 找到第一个让断言为真的元素
findLast 同上,逆序

随机取值

函数名 简介
sample 从集合中随机选出一个元素
sampleSize 从集合中随机选出 n 个元素

迭代

函数名 简介
reduce
reduceRight -

分组计数

函数名 简介
countBy 返回一个对象,属性名是迭代器的返回值,属性值该返回值出现的次数
groupBy 返回一个对象,属性名是迭代器的返回值,属性值是一个包含了相应元素的数组

未分类

函数名 简介
keyBy 返回一个对象,属性名是迭代器的返回值,属性值是元素本身
some 对于集合中的每个元素,是否至少其一返回真值
every 对于集合中的每个元素,是否都返回真值
includes 判断元素是不是在数组中,判断某个值是不是某个对象的属性值,判断一个字符串是不是包含在另一个字符串中
map 对集合的数组
invokeMap -

6 对象

仅需要部分字段

函数名 简介
omit 通过忽略某些字段创建一个新对象
omitBy
pick 通过指定某些字段创建一个新对象
pickBy -

合并对象

函数名 简介
assign 合并对象
assignWith 有条件地合并对象
extend 合并对象,包括原型链上的属性
extendWith 有条件地合并对象,包括原型链上的属性
assignIn 别名extend
assignInWith 别名extendWith
defaults 合并对象,将后面参数的属性付给第一个参数,如果第一个参数没有相应属性的话
defaultsDeep 递归地合并对象,将后面参数的属性付给第一个参数,如果第一个参数没有相应属性的话
merge 递归地合并对象,将后面参数的属性付给第一个参数
mergeWith merge,额外接受一个 customizer 参数

键值列表

函数名 简介
keys 创建一个数组,包含对象中所有的属性
keysIn 创建一个数组,包含对象中所有的属性(包含原型链上的)
functions 创建一个数组,包含对象中所有值为函数的属性
functionsIn 创建一个数组,包含对象中所有值为函数的属性(包含原型链上的)
values 创建一个数组,包含对象中所有的属性值
valuesIn 创建一个数组,包含对象中所有的属性值(包含原型链上的)

赋值取值

函数名 简介
at 获取对象的一组属性路径的值,肯定不会报错
get 获取对象的某个属性路径的值,肯定不会报错
result get,但是如果属性值是函数的话,自动执行该函数
set 设置对象的某个属性路径的值
setWith 设置对象的某个属性路径的值,遇到不存在的中间对象,使用数组呢?还是使用对象呢?等等
update set,只是接受一个函数作为参数
updateWith setWith,只是接受一个函数作为参数
unset 删除对象的某个属性路径
invoke 调用对象中某个属性路径上的函数,肯定不会报错

键值数组

函数名 简介
entries {‘a’:1}=>[[‘a’,1]]。别名toPairs
entriesIn 同上,但是包含原型链上的属性。别名toPairsIn

键值变换

函数名 简介
mapKeys 对对象中所有属性名做某种处理之后形成一个新对象
mapValues 对对象中所有属性值做某种处理之后形成一个新对象
invert 将对象中的属性名和属性值互换转成一个新对象
invertBy invert,但是转换以后的属性值是原属性值组成的数组

键值遍历

函数名 简介
forIn 遍历对象上的所有属性,包含原型链上的。
forInRight 遍历对象上的所有属性,包含原型链上的。
forOwn 遍历对象上的所有属性,不包含原型链上的。
forOwnRight 遍历对象上的所有属性,不包含原型链上的。

寻找属性

函数名 简介
findKey find类似,但是匹配的是对象的属性值,返回的是对象的属性名
findLastKey findKey类似,但是匹配的是对象的属性值,返回的是对象的属性名

判断属性是否存在

函数名 简介
has 判断对象上是否拥有某个属性,不包含原型链上的
hasIn 判断对象上是否拥有某个属性,包含原型链上的

转换对象或数组

函数名 简介
transform reduce,但是其迭代器函数返回的是布尔值,如果返回 false,则停止迭代

创建新对象

函数名 简介
create 创建一个对象,并指定其原型和属性

7. 函数

修改参数

函数名 简介
ary 创建一个包裹函数,只将前 n 个参数传递给原函数。
unary 创建一个包裹函数,只将第一个参数传递给原函数。
flip 创建一个包裹函数,将参数逆序之后传递给原函数。
rearg 创建一个包裹函数,调整参数顺序之后在传递给原函数
rest 创建一个包裹函数,将参数合成数组之后传递给原函数
spread 创建一个包裹函数,将数组参数展开之后传给原函数,跟rest相反
overArgs 创建一个包裹函数,将参数做处理之后再传递给原函数。

修改结果

函数名 简介
negate 创建一个包裹函数,返回原函数结果的非。

缓存结果

函数名 简介
memoize 创建一个包裹函数,会缓存计算结果

降频调用

函数名 简介
debounce
throttle

延迟调用

函数名 简介
defer 类似setTimeout(fn,0),可以指定参数
delay 类似setTimeout(fn,x),可以指定参数

延迟调用

函数名 简介
once 创建一个包裹函数,确保原函数只被执行一次。
before 创建一个包裹函数,确保原函数只被执行 n 次。
after 创建一个包裹函数,调用包裹函数时只有 n 次之后才会调用目标函数

固定参数

函数名 简介
wrap 创建一个包裹函数,固定原函数的第一个参数
partial 创建一个包裹函数,固定原函数若干个参数
partialRight 创建一个包裹函数,固定原函数若干个参数
bind 创建一个包裹函数,固定原函数若干个参数,并指定 this 对象
bindKey bind功能类似,但是能够处理尚未创建或被重写的函数,有点事件代理的感觉。
curry 创建一个包裹函数,可以传入任意数量的参数,如果参数不完整,则返回一个接受余下参数的新函数,否则,调用原函数获得计算结果。
curryRight 同上,逆序

8. 字符串

书写格式

函数名 简介
startCase 每个单词首字母大写,多用于标题
camelCase 小驼峰
kebabCase 小写连字符
snakeCase 小写下划线
upperCase 大写加空格
lowerCase 小写加空格

大写小写

函数名 简介
capitalize 首字母大写,其余小写
upperFirst 首字母大写,其余不变
lowerFirst 首字母小写,其余不变
toUpper 大写
toLower 小写

打头结尾

函数名 简介
endsWith 是不是以特定字符串结尾
startsWith 是不是以特定字符串打头

转义

函数名 简介
escape 转义 &<>”‘,与unescape相反
escapeRegExp 转义正则表达式中的特殊字符:^\$.*+?()[]{}\

补全抹掉

函数名 简介
pad 使用某个字符串将特定字符串扩充至指定长度,类似地还有padStartpadEnd
trim 去除字符串两边的特殊字符(默认为空格),类似地还有trimStarttrimEnd

未分类

函数名 简介
parseInt 转成整型
repeat 将某个字符串重复 n 遍
replace 替换字符串
split 拆分字符串
template 简单模板引擎
truncate 截断字符串
words 将字符串拆分成单词,可以指定拆分模式
deburr 基本拉丁字母

9. 数字

函数名 主要参数-返回值 简介
clamp 数字=>数字 将数字限定在一个范围内
inRange 数字=>布尔 判断数字是否在某个区间里
random 区间=>数字 随机获取一个值,可以通过第三个参数指定是不是返回小数

10. 数学

加减乘除

函数名 主要参数-返回值 简介
add 两个数字 => 数字 返回两个数字的和
subtract 两个数字 => 数字 返回两个数字的差
multiply 两个数字 => 数字 返回两个数字的积
divide 两个数字 => 数字 返回两个数字的商

和,最大值,最小值,平均值

函数名 主要参数-返回值 简介
sum 数组 => 数字 返回数组中的各数字之和
max 数组 => 数字 返回数组中的最大值
min 数组 => 数字 返回数组中的最小值
mean 数组 => 数字 返回数组中的平均值

数字精度

函数名 主要参数-返回值 简介
ceil 数字 => 数字 向上取整,可以指定精度
floor 数字 => 数字 向下取整,可以指定精度
round 数字 => 数字 四舍五入取整,可以指定精度

11. 语言

数值比较

函数名 简介
eq 等价于===
isEqual 深度比较对象是否相等
isEqualWith 深度比较对象是否相等,可以定义相等比较函数
gt 大于
lt 小于
gte 大于等于
lte 小于等于

类型判断

函数名 简介
isArguments
isArray
isArrayBuffer
isArrayLike
isArrayLikeObject
isBoolean
isBuffer
isDate
isElement
isEmpty 判断是否有可遍历的属性
isError 错误
isFinite 是否是有限的数字,基于 Number.isFinite
isFunction
isInteger
isLength
isMap
isMatch
isMatchWith
isNaN
isNative 原生函数
isNil 等价于 _.isNull(val) || _.isUndefined(val)
isNull
isNumber
isObject
isObjectLike
isPlainObject
isRegExp
isSafeInteger
isSet isSet
isString
isSymbol
isTypedArray
isUndefined
isWeakMap
isWeakSet

类型转换

函数名 简介
castArray 强制转给数组
toArray 转成数组,对象调用 Object.values,字符串转成字符数组
toFinite
toInteger
toLength
toNumber
toPlainObject
toSafeInteger
toString 转成字符串,

复制对象

函数名 简介
clone
cloneDeep
cloneDeepWith
cloneWith

检测对象

函数名 简介
conformsTo 判断一个对象的字段是否满足一些条件

12. 工具

总是返回某个参数的函数

函数名 简介
constant 创建一个包裹函数,总是返回第一个参数
nthArg 创建一个包裹函数,总是返回第 n 个参数

总是返回某个特定值的函数

函数名 简介
noop 总是返回undefined的函数
stubArray 总是返回空数组的函数
stubObject 总是返回空对象的函数
stubString 总是返回空字符串的函数
stubTrue 总是返回true的函数
stubFalse 总是返回false的函数
identity 总是返回第一个参数

获取对象的属性值或者调用对象的函数

函数名 简介
method _.invoke(object, path, [args])预设pathargs两个参数
methodOf _.invoke(object, path, [args])预设objectargs两个参数
property _.get(object, path)预设path参数,不同的是缺少defaultValue`参数
propertyOf _.get(object, path)预设object参数,不同的是缺少defaultValue参数

判断对象是否满足某些条件

函数名 简介
conforms 创建一个包裹函数,判断一个对象的字段是否满足某个函数。conforms意思是遵守。
matches 创建一个包裹函数,判断一个对象的字段是否等于某个值,使用isEqual判断是否相等。跟isMatch类似
matchesProperty 创建一个包裹函数,判断一个对象特定字段是否等于某个值,使用isEqual判断是否相等。

把多个操作合成一个操作

函数名 简介
flow 把一组函数串起来形成一个新函数
flowRight 同上,倒序

批量进行多个操作

函数名 简介
over 创建一个新函数,并将参数传递给预先指定的一组函数,并返回其结果
overEvery over类似,判断是不是所有函数都返回真值
overSome over类似,判断是不是至少一个函数返回真值

等差数列

函数名 简介
range 生成等差数列,可以指定步长,步长可以是小数,也可以是负数
rangeRight 这个基本可以忽略,功能完成可以由range代替。

其他未分类

函数名 简介
attempt 使用 try-catch 包裹函数,如果出错返回错误对象
bindAll 将一个对象的多个函数中的 this 固定为该对象
cond 创建一个拥有复杂 if-else 的函数
defaultTo 如果第一个参数为 NaN,null,undefined,则返回第二个参数,否则返回第一个参数
iteratee 创建一个迭代函数
noConflict 如果_被占用,可以使用该方法
runInContext 创建一个lodash镜像对象,可以扩展修改该对象
mixin 给一个对象的原型添加属性或方法,一般配合runInContext扩展lodash
times 执行函数 n 次,传入参数为 index
toPath ‘a[0].b.c’=>[‘a’,’0’,’b’,’c’]
uniqueId 生成唯一 ID,可以指定前缀

13. 链式

链式调用的好处

省略了中间变量,让代码更加简洁,更加安全。
链式调用可以优化成惰性求值(延迟计算),让代码更加高效。

_(value)

创建一个经过 lodash 包装过后的对象会启用隐式链,直到调用了不支持链接调用的函数或者主动调用value方法解除链式调用。
作用类似于chain

lodash 包装对象上的特殊函数

函数名 简介
tap 可以在链式调用中插入普通方法,直接修改中间结果,也可以仅仅是用于调试打印中间结果
thru tap,但是使用函数的返回值作为中间结果
commit 立即执行链式调用中尚未进行的操作
next 获得包装对象的下一个值
plant 复制一个链式调用,并传入初始值
value 结束链式调用,并计算结果。别名valueOftoJSON