携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
使用说明
lodash里的random方法是原生Math.random的增强,它允许产生一个包括 lower 与 upper 之间的数。
如果只提供一个参数返回一个0到提供数之间的数。
如果 floating 设为 true,或者 lower 或 upper 是浮点数,结果返回浮点数。
_.random(0, 5);
// => 产生介于0和5之间的随机整数
_.random(5);
// => 同样产生介于0和5之间的随机整数
_.random(5, true);
// => 产生介于0和5之间的随机浮点数
_.random(1.2, 5.2);
// => 产生介于1.2和5.2之间的随机浮点数
_.random()
// => 输出0或1或NaN
源码实现
isIterateeCall
在讲random源码实现之前,我们需要了解一个内部方法isIterateeCall,该方法可以通过传入的参数判断目标是否属于可迭代调用类型。
其判断逻辑如下:
- 首先对传入的第三个参数进行判断,如果不是对象类型则返回false。
- 如果不是数字类型或者字符串类型则返回false,如果是则进入下一步处理。
- 对于数字类型,会调用isArrayLike方法判断第三个参数,会调用isIndex方法判断对比第二个参数和第三个参数的长度。如果不成立则返回false,如果是则调用eq方法返回其结果。
- 对于字符串类型,也会调用eq方法返回其结果。
其中的方法说明:
- eq方法主要判断两个参数是否相等,其中NaN值在eq内部会判断为相等并返回true。
- isIndex方法会判断第一个参数是否在0和第二个参数的值之间,即判断第一个参数是否属于某个数组的索引。
- isObject在之前的篇章讲解过了,这里就不再过多赘述。
import isArrayLike from './isArrayLike.js';
import isObject from './isObject.js';
var MAX_SAFE_INTEGER = 9007199254740991;
var reIsUint = /^(?:0|[1-9]\d*)$/;
function isIndex(value, length) {
var type = typeof value;
length = length == null ? MAX_SAFE_INTEGER : length;
return !!length &&
(type == 'number' ||
(type != 'symbol' && reIsUint.test(value))) &&
(value > -1 && value % 1 == 0 && value < length);
}
function eq(value, other) {
return value === other || (value !== value && other !== other);
}
function isIterateeCall(value, index, object) {
if (!isObject(object)) {
return false;
}
var type = typeof index;
if (type == 'number'
? (isArrayLike(object) && isIndex(index, object.length))
: (type == 'string' && index in object)
) {
return eq(object[index], value);
}
return false;
}
random
random方法在实现上主要进行如下处理:
- 对参数进行大量的判断,主要是先对第三个参数floating进行判断处理,第三个参数floating存在的话会判断其数据类型,当其类型不是boolean类型时,继续判断三个参数是否属于可迭代类型,如果是的话则对第一个参数和第二个参数赋值为undefined。
- 如果第三个参数为undefined,会继续判断第一个参数或第二个参数是否存在布尔值的情况,相继进行值的初始化处理。
- 如果第三个参数存在,会对第一个数调用toFinite方法,然后依次对相应的参数进行赋值。
- 如果第一个参数比第二个参数小,调换双方的值,随机数的产生依托取得得目标整数加上浮点数,而浮点数处理主要靠原生的parseFloat方法。
- 当不需要浮点数时,则调用内部的baseRandom方法返回其结果。
// 参数理想的情况下,浮点随机数的核心实现
Math.min(lower + (Math.random() * (upper - lower + parseFloat('1e-' + ((Math.random() + '').length - 1)))), upper);
// lodash内部方法baseRandom的实现
function baseRandom(lower, upper) {
return lower + Math.floor(Math.random() * (upper - lower + 1));
}
下面为lodash里random方法的实现:
import baseRandom from './_baseRandom.js';
import isIterateeCall from './_isIterateeCall.js';
import toFinite from './toFinite.js';
var freeParseFloat = parseFloat;
var nativeMin = Math.min,
nativeRandom = Math.random;
function random(lower, upper, floating) {
if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {
upper = floating = undefined;
}
if (floating === undefined) {
if (typeof upper == 'boolean') {
floating = upper;
upper = undefined;
}
else if (typeof lower == 'boolean') {
floating = lower;
lower = undefined;
}
}
if (lower === undefined && upper === undefined) {
lower = 0;
upper = 1;
}
else {
lower = toFinite(lower);
if (upper === undefined) {
upper = lower;
lower = 0;
} else {
upper = toFinite(upper);
}
}
if (lower > upper) {
var temp = lower;
lower = upper;
upper = temp;
}
if (floating || lower % 1 || upper % 1) {
var rand = nativeRandom();
return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);
}
return baseRandom(lower, upper);
}
小结
本篇章我们讲解了lodash里random方法的实现,其借助了isIterateeCall判断方法,在实现上对参数的多种情况进行了大量判断,确保代码逻辑的合理性。