携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
方法说明
divide方法为两个数相除。第一个参数为除数,类型为number;第二个参数为被除数,类型为number。
具体使用如下:
_.divide()
// => 1
_.divide(6, 4);
// => 1.5
_.divide('2',2)
// => 1
_.divide('2','2')
// => 1
_.divide('2',[1,2,3])
// => NaN
_.divide([],1)
// => 0
_.divide(()=>{},1)
// => NaN
在前面的篇章《 lodash里的add》 ,我们可以根据add方法实现的原理,借助原生/符号去进行除法,源码内部依旧有容错判断,对于非数字类型有相应的处理。
源码实现
第一步,首先我们像前面篇章的add方法实现一样,先创建工厂函数,对该方法赋予默认值。
const divide = createMathOperation((dividend, divisor) => dividend / divisor, 1)
在这里我们取名和add方法的工厂函数命名一样,取为createMathOperation,取默认值为1。(其实就是对于数字操作单独提炼了一个名为createMathOperation的方法)
第二步,容错处理。我们需要对两个参数进行判断,非数字类型进行相应处理。(这里其实就是和add方法公用的处理逻辑。)
function createMathOperation(operator, defaultValue) {
return (value, other) => {
if (value === undefined && other === undefined) {
return defaultValue
}
if (value !== undefined && other === undefined) {
return value
}
if (other !== undefined && value === undefined) {
return other
}
if (typeof value === 'string' || typeof other === 'string') {
value = baseToString(value)
other = baseToString(other)
}
else {
value = baseToNumber(value)
other = baseToNumber(other)
}
return operator(value, other)
}
}
这里关键还是调用传入的operator回调函数,因为operator回调函数里便定义了除法操作。
对于有至少一个为字符串类型的,则参数都转为字符串,其余情况将数据转为数字类型。
借由/符号的隐式转换,结果均为数字类型。
对于baseToString方法和baseToNumber方法的实现,在之前实现add方法的篇章中已经讲解其实现,这里就不再赘述了,通过其语义便知其功能。
下面为divide的完整源码:
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 baseToString(value) {
if (typeof value === 'string') {
return value
}
if (Array.isArray(value)) {
return `${value.map(baseToString)}`
}
if (isSymbol(value)) {
return symbolToString ? symbolToString.call(value) : ''
}
const INFINITY = 1 / 0
const result = `${value}`
return (result === '0' && (1 / value) === -INFINITY) ? '-0' : result
}
function baseToNumber(value) {
if (typeof value === 'number') {
return value
}
if (isSymbol(value)) {
const NAN = 0 / 0
return NAN
}
return +value
}
function createMathOperation(operator, defaultValue) {
return (value, other) => {
if (value === undefined && other === undefined) {
return defaultValue
}
if (value !== undefined && other === undefined) {
return value
}
if (other !== undefined && value === undefined) {
return other
}
if (typeof value === 'string' || typeof other === 'string') {
value = baseToString(value)
other = baseToString(other)
}
else {
value = baseToNumber(value)
other = baseToNumber(other)
}
return operator(value, other)
}
}
const divide = createMathOperation((dividend, divisor) => dividend / divisor, 1)
其实单纯实现一个加法或者除法并不需要定义工厂函数,但是在实现后续的乘法,减法中我们会发现都是一样的逻辑判断处理,为了方便代码抽离与封装,以及提高可读性,所以lodash源码抽象出一个数字操作的工厂函数,方便后续代码实现封装。
小结
本篇章主要讲解lodash源码里divide方法的实现,其主要借助/符号进行处理,该方法在原生除法的基础上做了大量的容错处理,由于隐式转换,所以该方法返回的结果都是数字类型。
同时我们意识到对于重复的操作,lodash做了提炼封装,抽离出一个createMathOperation的工厂函数,用于常见的数字操作,主要是对参数的类型进行严谨性判断和验证。
所以在实际项目中,当重复逻辑出现两次及以上时,就可以考虑创建一个工厂函数去处理通用逻辑。