pocket-lodash
pocket-lodash copied to clipboard
lodash源码分析之toNumber
本文为读 lodash 源码的第五十二篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
依赖
import isObject from './isObject.js'
import isSymbol from './isSymbol.js'
源码分析
整体源码
const NAN = 0 / 0
/** Used to match leading and trailing whitespace. */
const reTrim = /^\s+|\s+$/g
/** Used to detect bad signed hexadecimal string values. */
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i
/** Used to detect binary string values. */
const reIsBinary = /^0b[01]+$/i
/** Used to detect octal string values. */
const reIsOctal = /^0o[0-7]+$/i
/** Built-in method references without a dependency on `root`. */
const freeParseInt = parseInt
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)
}
几个常量
const NAN = 0 / 0
/** Used to match leading and trailing whitespace. */
const reTrim = /^\s+|\s+$/g
/** Used to detect bad signed hexadecimal string values. */
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i
/** Used to detect binary string values. */
const reIsBinary = /^0b[01]+$/i
/** Used to detect octal string values. */
const reIsOctal = /^0o[0-7]+$/i
/** Built-in method references without a dependency on `root`. */
const freeParseInt = parseInt
NAN
NaN 的意思是 not a number ,用 0 / 0 可以返回一个 NaN 值。
reTrim
这个正则用来去除前后的空格
reIsBadHex
这个正则咋看起来有点奇怪,是检测形似带符号的16进制字符串,如: +0x1f 。为什么是 bad 呢,从 lodash 的pr来看,应该是跟 node v0.8 的某个 bug 有关。因为 lodash 也支持在 node ,因此需要做兼容。所以十六进制的处理跟二进制和八进制不太一样。
resIsBinary
形似二进制的字符串正则,二进制以 0b 开头,后面只能是 0 或者 1 。
reIsOctal
形似八进制的字符串正则,八进制以 0o 开头,后面只能跟 0-7 的数字。
处理number类型
if (typeof value === 'number') {
return value
}
如果为 number 类型,则不需要做转换,原样返回即可。
处理Symbol类型
if (isSymbol(value)) {
return NAN
}
如果为 Symbol 类型,则直接返回 NAN 。
处理Object类型
if (isObject(value)) {
const other = typeof value.valueOf === 'function' ? value.valueOf() : value
value = isObject(other) ? `${other}` : other
}
规范中有规定,object 类型有 valueOf 的方法,这个方法可以返回对象的原始值,默认情况下,如果 valueOf 没有返回原始值,则会返回对象本身。
例如 Number(1) 的 valueOf 会返回数字 1 。以下的例子会返回 a :
const obj = {
a: 'a',
valueOf () {
return 'a'
}
}
因此,首先判断 value 有没有存在 valueOf 函数,如果有,则先调用 valueOf 得出结果。
但是,value 可能会没有 valueOf 函数,或者 valueOf 函数也可能会返回一个对象,这时,需要将 other 转换成 string 类型,留待后面处理。
处理非String类型
if (typeof value !== 'string') {
return value === 0 ? value : +value
}
在处理完 Object 类型后,现在 value 都是基本类型了,在这个阶段,本来可以直接用 +value 来转换成 number 类型的,但是字符串前后可能会有空格,直接用 +value 来转换的话,会得出 NaN 值,因此字符串需要单独处理。
处理String类型
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)
第一步先将 value 的前后空格移除。
接下来处理二进制和八进制,直接用 parseInt 来做转换,parseInt 的第二个参数来指定进制,其实二进制、八进制也可以用 +value 来转换的,有也人提了 pr,而且性能更好。
本来 16 进制也可以用 parseInt 来处理的,但是根据 lodash 的提交来推测,用 parseInt 来转换形似 +0x16 或 -0x16 的字符串时,会有bug,这样的字符串,预期应该返回 NAN 。
如果不是形似二进制、八进制或带符号的十六进制字符串,则使用 +value 来做转换。
参考资料
MDN: Object.prototype.valueOf()
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见: 
作者:对角另一面
处理object类型那个if能举个例子吗,貌似这段删掉对最后结果也不影响?