偶然看到了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的检测方式
- 普通的Symbol用typeof即可
- 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对象的处理方式
- 检测是否存在valueOf方法,有则调用
- 如果valueOf是基本值就赋值给value
- 如果不是基本值就调用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)。
总结一下对字符串的处理
- 去除收尾空格
- 检测是否为二进制,八进制,等几种情况,如果是就调用parseInt。
- 最后调用一元+尝试转化为数字