【lodash源码阅读】0 从toNumber看lodash的类型检测


偶然看到了lodash的类型检测,感觉很细致,这次仔细分析一下

先贴上toNumber的代码

const reTrim = /^\s+|\s+$/g
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i
const reIsBinary = /^0b[01]+$/i
const reIsOctal = /^0o[0-7]+$/i

function toNumber(value) {
  if (typeof value === 'number') {
    return value
  }
  if (isSymbol(value)) {
    return NAN
  }
  if (isObject(value)) {
    const other = typeof value.valueOf === 'function' ? value.valueOf() : value
    value = isObject(other) ? `${other}` : other
  }
  if (typeof value !== 'string') {
    return value === 0 ? value : +value
  }
  value = value.replace(reTrim, '')
  const isBinary = reIsBinary.test(value)
  return (isBinary || reIsOctal.test(value))
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
    : (reIsBadHex.test(value) ? NAN : +value)
}

首先一句话概括一下toNumber的作用,尽最大可能将参数转化为number,如果无法转化就返回NaN

首先用typeof检测一下最简单的number,如果是number返回即可。

然后检测es6新加入的symbol,看一下isSymbol的代码, symbol不能转化为number,直接返回NaN即可

function isSymbol(value) {
    const type = typeof value

    /**
     * const sym = Symbol("foo");
     * typeof sym;     // "symbol"
     * const symObj = Oject(sym);
     * typeof symObj;  // "object"
     */
    return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]')
}

用typeof检测普通的symbol返回的就是’symbol’, 另一种特殊情况就是下面这样

let s = Symbol("symbol")
let os = Object(s)
typeof os // Object
Object.prototype.toString.call(os)  //[object Symbol]

使用Object封装过的Symbol需要用getTag进行检测

总结一下Symbol的检测方式

  1. 普通的Symbol用typeof即可
  2. Object封装的Symbol用Object.prototype.toString.call(Symbold)

接下来是对Object的检测,看一下isObject的源码

function isObject(value) {
    const type = typeof value
    return value != null && (type === 'object' || type === 'function')
}   

typeof Object的返回值有两种比较特殊的情况,null和function,function也属于Object。如果isObject返回true,调一下下面的代码

const other = typeof value.valueOf === 'function' ? value.valueOf() : value
value = isObject(other) ? `${other}` : other

为了尽力转化为number,先调一下valueOf(这个时候还检测valueOf是否是function)。然后看一下valueOf的返回值是否是基本值,这里的模板字符串相当于调用toString方法

总结一下对于object对象的处理方式

  1. 检测是否存在valueOf方法,有则调用
  2. 如果valueOf是基本值就赋值给value
  3. 如果不是基本值就调用toString,赋值给value

然后就是对boolean以及其他基本值的检测(主要是number,这个number可以在object检测时的valueOf产生),看一下这一部分

if (typeof value !== 'string') {
    return value === 0 ? value : +value
}

+运算符会将value转化为number

最后就是string的处理

value = value.replace(reTrim, '')
const isBinary = reIsBinary.test(value)
return (isBinary || reIsOctal.test(value))? parseInt(value.slice(2), isBinary ? 2 : 8): (reIsBadHex.test(value) ? NAN : +value)

为了尽力转化为number,首先用replace去除收尾的空格。接下来就是检测二进制,八进制,十六进制,然后使用parseInt进行转化即可(十六进制这个地方据说是node老版本的一个bug)。

总结一下对字符串的处理

  1. 去除收尾空格
  2. 检测是否为二进制,八进制,等几种情况,如果是就调用parseInt。
  3. 最后调用一元+尝试转化为数字

summary

summary


Author: Maple
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Maple !
  TOC