携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
使用说明
lodash里的max方法主要是计算参数中的最大值。 如果参数是空的或者假值将会返回undefined。
参数说明:参数只有一个,需要是数组类型。
_.max(1)
// => undefined
_.max(1,2)
// => undefined
_.max([])
// => undefined
_.max([1])
// => 1
_.max([2,1,3])
// => 3
_.max([1,2,{},4])
// => 4
_.max([10,'a',{},4])
// => 10
_.max([99999,{}])
// => 99999
_.max({})
// => undefined
_.max(['a',{}])
// => a
求指定数组的最大值
对于指定数组,如果我们想要知道求其最大值,那么就需要遍历每一个值,在每次遍历的时候去比较和存储,思路如下:
function getMaxValue(array) {
if (!Array.isArray(array)) return undefined
const length = array.length;
let maxValue = array[0]
for (let i = 1; i < length; i++) {
if (maxValue < array[i]) {
maxValue = array[i]
}
}
return maxValue
}
上述代码实现了求指定数组的最大值,而主要逻辑在于最大值的存储和比较。
源码实现
下面我们来看看lodash里的求最大数的源码实现,首先我们从入口函数分析:
function max(array) {
return (array && array.length)
? baseExtremum(array, identity, baseGt)
: undefined;
}
上面的代码我们可以看到max方法首先会对参数进行分析,对于参数不存在或者参数存在但是身上没有length属性的情况,直接返回undefined。如果参数存在并且身上存在length属性,则做下一步的处理。
所以我们可以看出,虽然官方文档说参数是array数组的形式,但是我们依旧可以传入字符串:
_.max('hello')
// => 'o'
_.max('1342')
// => '4'
_.max('你好呀')
// => '好'
可见,该方法是可以比较字符串参数的。
回归正题,我们看到如果条件成立,其内部还会调用baseExtremum方法,baseExtremum方法的第一个参数是使用者传入的参数,而identity和baseGt则是传递给baseExtremum使用的方法。
baseExtremum方法字面的意思就是求极值的意思,这里进一步封装成方法就是为了达到复用的目的。
identity方法会返回它接收到的第一个参数,这种写法在于语义化操作,用于获取一个变量的值,源码实现如下:
function identity(value) {
return value;
}
baseGt主要是比较两个值的大小并返回布尔值,并不强制参数的类型,源码实现如下:
function baseGt(value, other) {
return value > other;
}
对于参数的遍历,loadsh源码采用while循环,每次索引值增加,对数组进行对比和验证处理。
这里运用了var关键字的变量提升,current最开始存储array[0],即参数第一项的值,然后通过调用传入的对比方法comparator,只有满足current>computed时进入判断里赋值。
在对比的过程中,我们并不确定数组里的每一项都是有效的值,如果某一项是null的话,那将进入不了判断。
value和current本质是同一个东西。只是为了在语义上区分,value保留的是原始的数据源,而current则作为比较去操作的数据。
function baseExtremum(array, iteratee, comparator) {
var index = -1,
length = array.length;
while (++index < length) {
var value = array[index],
current = iteratee(value);
if (current != null && (computed === undefined
? (current === current && !isSymbol(current))
: comparator(current, computed)
)) {
var computed = current,
result = value;
}
}
return result;
}
在第一次执行if语句时,由于变量提升,computed初始值为undefined,所以执行判断current的值是否属于Symbol类型,Symbol类型判断的基础方法实现在之前的篇章已经讲解,这里就不做赘述了。
下面为完整max方法实现。
function isSymbol(value) {
const toString = Object.prototype.toString
function getTag(value) {
if (value == null) {
return value === undefined ? '[object Undefined]' : '[object Null]'
}
return toString.call(value)
}
const type = typeof value
return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}
function identity(value) {
return value;
}
function baseGt(value, other) {
return value > other;
}
function baseExtremum(array, iteratee, comparator) {
var index = -1,
length = array.length;
while (++index < length) {
var value = array[index],
current = iteratee(value);
if (current != null && (computed === undefined
? (current === current && !isSymbol(current))
: comparator(current, computed)
)) {
var computed = current,
result = value;
}
}
return result;
}
function max(array) {
return (array && array.length)
? baseExtremum(array, identity, baseGt)
: undefined;
}
小结
从源码分析可见lodash对于参数的类型的接收是宽泛的,而在内部处理上又是严谨的。
max方法不仅可以对数组进行操作,还可以对于字符串进行相应的比较操作。
同时lodash也抽离出比较操作方法baseGt,主要是判断第一个参数是否大于第二个参数。