上篇文章分析了在 Lodash 中如何可靠地检测对象类型(纯对象)。在 JS 中,引用类型不仅仅有纯对象,常见的还有数组、函数、类数组对象等等。除了引用类型,还有一个大类叫做基本数据类型,包括数值、字符串、布尔值、null、undefnied、Symbol。
所以,在这一篇文章中,就要来分析在 Lodash 中如何判断其他常见的数据类型的?以及在原生 JS 中,这些数据类型又存在哪些“坑”?
Part 1. 数值、字符串、布尔值、Symbol
先来看这三个最基本的类型。
OK,它们三种类型可以被 typeof 操作符准确地检测。但是,在分析 Lodash 源码时,我发现 Lodash 可不是只利用 typeof 进行地检测。
function isNumber(value) {
return typeof value == 'number' ||
(isObjectLike(value) && baseGetTag(value) == '[object Number]')
}
先看 isNumber() 函数。可以看到,Lodash 用到了 isObjectLike() 和 baseGetTag() 函数,这两个函数在上一篇文章中已经分析过了。
function isString(value) {
const type = typeof value
return type == 'string' || (type == 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]')
}
function isBoolean(value) {
return value === true || value === false ||
(isObjectLike(value) && baseGetTag(value) == '[object Boolean]')
}
再看看,我们发现 Lodash 在判断数值、字符串、布尔值都用到了 isObjectLike() 和 baseGetTag() 函数。Why?
这得从 JS 语言本身来考虑。
在 JS 中,可以通过字面量的形式来直接创建数值、字符串、布尔值:
var num = 1;
var str = 'a';
var flag = true;
这种形式创建的数据均可以直接被 typeof 操作符检测,不会存在歧义:
typeof num // => number
typeof str // => string
typeof flag // => boolean
但是,JS 允许我们通过内置函数 Number()、String()、Boolean() 来创建相应的数据,只不过这时用 typeof 检测不到,只能返回 ‘object’:
typeof new Number(1) // => object
typeof new String('a') // => object
typeof new Boolean(true) // => object
这从开发者的感知上会产生歧义,这是原生 JS 存在的问题。问题的原因在于 new 操作符本身的原理是创建一个对象并返回(这里不展开,会在其他专题中总结),所以导致了 typeof 检测为 ‘object’。
在开发中最佳实践是要求我们避免这样创建基本数据类型。但是,Lodash 采用了主动出击的方式,在源码中认为以 new 形式创建的基本数据类型依旧应该是相应的数据类型。这就解释了这一行代码存在的意义:
isObjectLike(value)