携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
前言
lodash的Number类有三个方法,分别为:clamp、inRange、random。
本篇章主要讲解clamp、inRange方法以及inRange方法内部实现借助的toFinite方法。
使用说明
clamp
clamp方法可以返回限制在lower
和upper
之间的值,实际上就是先对比第一第二个数,去计算最大的那一个值,然后用这个计算了的值再去对比第三个数,取最小的值并返回。
_.clamp(-10, -5, 5);
// => -5
_.clamp(10, -5, 5);
// => 5
_.clamp()
// => NaN
_.clamp('3',1,2)
// => 2
_.clamp('a',1,2)
// => NaN
inRange
clamp方法可以检查 n
是否在 start
与 end
之间,但不包括 end
。 如果 end
没有指定,那么 start
设置为0
。 如果 start
大于 end
,那么参数会交换以便支持负范围。
_.inRange(3, 2, 4);
// => true
_.inRange(4, 8);
// => true
_.inRange(4, 2);
// => false
_.inRange(2, 2);
// => false
_.inRange(1.2, 2);
// => true
_.inRange(5.2, 4);
// => false
_.inRange(-3, -2, -6);
// => true
_.inRange()
// => false
_.inRange({})
// => false
手动实现
clamp
对于实现一个clamp方法,我们可以做如下处理:
function clamp(number, lower, upper) {
number = +number
lower = +lower
upper = +upper
if (typeof number === "number" && number !== NaN) {
number = number > lower ? number : lower
number = number < upper ? number : upper
return number
}
return NaN
}
在实现上,我们先对参数进行类型转换,均转换为数字类型,对于NaN的数据我们不做处理直接返回NaN结果。
当参数均为数字时,我们先比较参数一和参数二,二者取最大的那个数进行赋值,然number参数进行存储;其次比较存储赋值之后的计算值number与参数三,二者取最小的结果并进行赋值存储然后返回结果。
inRange
实现inRange方法,我们可以进行如下处理:
- 先判断end是否为undefined,如果是undefined则将第二个参数start赋值为0,第三个参数end赋值为第二个参数的值。
- 将所有数值转换为数字类型,如果其中有数字为NaN则返回false。
- 只有当第一个参数介于第二个参数和第三个参数之间才返回true。
function inRand(number, start, end) {
if(end === undefined){
end = start
start = 0
}
number = +number
start = +start
end = +end
if(number !== number ||start !== start || end !== end) return false
if(number > start && number < end) return true
return false
}
源码实现
clamp
lodash里的clamp方法实现源码里,先对参数进行处理。主要是对非正常的数据进行赋值。
对于第二个参数lower和第三个参数upper的处理如下:
- 第一步:如果第三个参数不存在,则将第二个参数赋值给第三个参数,然后第二个数赋值为undefined。
- 第二步:如果第三个参数存在,则将第三个参数转换为数字类型,由于NaN是数字类型,但是NaN不等于NaN,所以源码里通过该特性来处理NaN的情况,如果转换后为NaN则将第三个参数赋值为0。
- 第三步,重复第二步的设置,只是操作的对象数据是第二个参数。
对于源数据,则调用toNumber方法将其转换为数字类型的数据。
下面为lodash实现clamp方法的源码:
function clamp(number, lower, upper) {
if (upper === undefined) {
upper = lower;
lower = undefined;
}
if (upper !== undefined) {
upper = toNumber(upper);
upper = upper === upper ? upper : 0;
}
if (lower !== undefined) {
lower = toNumber(lower);
lower = lower === lower ? lower : 0;
}
return baseClamp(toNumber(number), lower, upper);
}
inRange
在讲inRange方法之前需要提到toFinite方法的实现。 toFinite方法是暴露出去的方法,可以将参数转换为一个有限的数字。
_.toFinite(3.2);
// => 3.2
_.toFinite(Number.MIN_VALUE);
// => 5e-324
_.toFinite(Infinity);
// => 1.7976931348623157e+308
_.toFinite('3.2');
// => 3.2
实现如下:
- 首先对参数进行非0判断,是0的话返回0,不是0则调用toNumber将参数转换为数字类型,然后进行下一步处理。
- 其次判断参数时正无穷还是负无穷,是正数还是负数,最后返回相应的最大值。
- 最后进行NaN判断,NaN返回0,正常数据则返回转换后的数字类型结果。
import toNumber from './toNumber.js';
var INFINITY = 1 / 0,
MAX_INTEGER = 1.7976931348623157e+308;
function toFinite(value) {
if (!value) {
return value === 0 ? value : 0;
}
value = toNumber(value);
if (value === INFINITY || value === -INFINITY) {
var sign = (value < 0 ? -1 : 1);
return sign * MAX_INTEGER;
}
return value === value ? value : 0;
}
inRange方法在实现上进行如下处理:
- 先处理第二个参数,调用toFinit方法,将其转换为有限的数字类型值。
- 判断第三个参数,如果第三个参数是undefined则将第二个参数赋值为0,第三个参数赋值为第二个参数的值。否则的话则将第三个参数调用toFinite方法转换为有限的数字类型值。
- 同样的,将第一个参数转换为数字类型。
- 调用封装的baseInRange方法进行处理,baseInRange内部是对两个数值进行对比判断。
下面为lodash实现inRange方法的源码:
var nativeMax = Math.max, nativeMin = Math.min;
function baseInRange(number, start, end) {
return number >= nativeMin(start, end) && number < nativeMax(start, end);
}
function inRange(number, start, end) {
start = toFinite(start);
if (end === undefined) {
end = start;
start = 0;
} else {
end = toFinite(end);
}
number = toNumber(number);
return baseInRange(number, start, end);
}
小结
本篇章手写了clamp和inRange方法实现,然后通过分析其实现源码,发现内部对多个方法进行封装,其中inRange方法在实现上调用了对外暴露的toFinite方法。
toFinite方法则是对数据进行数字类型转换并限制在有限范围内。