类型判断

前言

在 web 开发和前端工具类库中,我们能够看到类型判断的广泛应用。

类型判断,顾名思义就是判断一个值是简单类型还是复杂类型。接下来让我们来学习下如何在代码中实现类型判断。

typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

我们知道在 ES6 之前,JavaScript 有 6 种数据类型,分别是:Undefined、Null、Boolean、Number、String、Object。通过下面例子,观察下它们被判断成什么结果。

console.log(typeof bar) // undefined
console.log(typeof null) // object
console.log(typeof true) // boolean
console.log(typeof 42) // number
console.log(typeof 'foo') // string
console.log(typeof {}) // object
console.log(typeof function baz() {}) // function

我们发现这上面有两个特殊的地方,一个是 null 被判断成是 Object 类型,另一个是 function 类型,它不在 6 种数据类型内。虽然 typeof 可以检测出 JavaScript 的六种数据类型,但是 Object 下还有很多细分的类型,如 Array、Function、Date、RegExp、Error 等。

举个例子看下检测情况:

console.log(typeof new Date()) // object
console.log(typeof new Array()) // object

咦?Object 的细分类型该怎么区分呢?答案是 Object.prototype.toString。

Object.prototype.toString

toString() 方法返回一个表示该对象的字符串。

每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。

我们使用 toString() 方法查看上面的 Date 和 Array 的类型:

const date = new Date()
const array = new Array()

console.log(Object.prototype.toString.call(date)) // [object Date]
console.log(Object.prototype.toString.call(array)) // [object Array]

因此我们可以使用 toString() 方法来区分 Object 的细分类型。

typeof 虽然不能区分 object 的细分类型,但是它的性能比较好。具体见下面的测试结果图:

is_type1

测试网站戳这里哈~在新窗口打开

isString

我们跟着 lodash在新窗口打开 库学习下如何实现字符串检测,下面贴出相关源码:

const toString = Object.prototype.toString

function getTag (value) {
    if (value == null) {
        return value === undefined ? '[object Undefined]' : '[object Null]'
    }
    return toString.call(value)
}

function isString (value) {
    const type = typeof value
    return type === 'string' ||
        (type === 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]')
}

// demo
const a = new String('hello')
const b = 'world'

console.log(isString(a)) // true
console.log(isString(b)) // true

分析:从源码可以得出,lodash 为了追求性能首先是使用 typeof 操作符进行检测,如果这一步不能判断出具体类型,再使用 Object.prototype.toString() 方法进行检测。

这里可能有同学产生疑问,咦?为什么源码中出现了 !Array.isArray(value) 这个条件判断?本人推测是性能方面的考虑。因为 Array.isArray() 的运行性能比 Object.prototype.toString() 优,所以避免使用 Object.prototype.toString 判断不是 string 类型的值。

isObject

我们跟着 lodash 库学习下如何实现对象检测,下面贴出相关源码:

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

分析:首先排除了 null 这个值,因为上面提过它的 typeof 检测值是 object,然后就是不要漏了函数类型,因为函数是对象的细分类,而它的 typeof 检测值是 function,最后返回判断结果值。

结语

本文到这里就结束了。在 lodash 库里还有其它类型检测的方法,感兴趣的同学可以去阅读源码,类型判断的大体思路是我上面介绍的 typeofObject.prototype.toString 方法。