JavaScript 类型判断

133 阅读3分钟

JavaScript 类型判断

JavaScript 的数据类型包括原始类型和对象类型:

原始类型:Null、Undefined、Number、String、Boolean、Symbol、BigInt

对象类型:Object (Array、Date、Function、RegExp、Math、Data、Set、Map)

1. typeof

功能:

  1. typeof 可以判断除了 null 以外的原始类型。
  2. typeof 只能判断对象类型中的 Function,其他判断不出来,都为 object。

说明: 主要用于判断基本数据类型,不能用于判断对象的具体类型 (不推荐用于类型判断)

typeof 检测 null 时返回 object,是最初 JavaScript 语言的一个 Bug,为了兼容老代码一直保留至今

typeof 'a' // 'string'
typeof 1   // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol('a') // 'symbol'
typeof 1n // 'bigint'
typeof null // 'object'
typeof function() {} // 'function'
typeof [] // 'object'
typeof {} // 'object'
// 特例情况
typeof NaN // 'number'
NaN !== NaN // true

2. instanceof

功能:检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

使用 a instanceof B 判断的是:a 是否为 B 的实例,即 a 的原型链上是否存在 B 的构造函数

说明:

  • instanceof 可以准确判断对象(引用)类型,但是不能准确检测原始类型
  • instanceof 可以随意修改原型的指向导致检测结果不准确 (不推荐用于类型判断)
1 instanceof Number // false
Number(1) instanceof Number // false
new Number(1) instanceof Number // true
[] instanceof Array // true
[] instanceof Object // true
null instanceof Object // true
{} instanceof Object // true
// 可修改原型链上,不可靠
const Fn = function () {}
Fn.prototype = Object.create(Array.prototype)
(new Fn()) instanceof Array // true
// 实现 instanceof
const instanceofFn = (obj, Constructor) => {
    if (typeof obj !== 'object' || obj === null) return false
    let proto = Reflect.getPrototypeOf(obj)
    while (true) {
        if (proto === null) return false
        if (proto === Constructor.prototype) return true
        proto = Reflect.getPrototypeOf(proto)
    }
}

3. constructor

功能:检测对象(基础类型)是由哪个构造函数生成

说明:

  • 除了 null 和 undefined,constructor 可以正确检测出原始类型和对象(引用)类型
  • 可以随意修改 constructor 导致检测结果不准确 (不推荐用于类型判断)
console.log((1).constructor === Number) // true
console.log((11n).constructor === BigInt) // true
console.log((Symbol('a')).constructor === Symbol) // true
console.log(Symbol.constructor === Function) // true
console.log({}.constructor === Object) // true
console.log([].constructor === Array) // true
console.log([].constructor === Object) // false
console.log(/abc/g.constructor === RegExp) // true
console.log((new Date()).constructor === Date) // true
console.log((() => { }).constructor === Function) // true
console.log((null).constructor === Function) //  Cannot read property 'constructor' of null
console.log((undefined).constructor === Function) // Cannot read property 'constructor' of undefined

const a = {}
a.constructor = {}
a.constructor === Object // false
a.prototype = { constructor: Array }
a.constructor === Object // false

4. Object.prototype.toString.call()

Object.prototype.toString() 方法返回一个表示该对象的字符串,可以改变它的 this 指向,将 this 指向要检测的值,即可返回当前检测值的信息

[[Class]]是一个内部属性,是一个字符串值,表明了该对象的类型,所有的对象(原生对象和宿主对象)都拥有该属性

Object.prototype.toString 方法被调用时,会执行下面的操作步骤:

  1. 如果 this 的值为 undefined ,则返回 '[object Undefined]'
  2. 如果 this 的值为 null ,则返回 '[object Null]'
  3. 获取 this 对象的 [[Class]] 属性的值,如 'Number'
  4. 计算出三个字符串 '[object ', 第一步的操作结果, 以及 ']' 连接后的新字符串,如 '[object Number]'
  5. 返回第三步的操作结果
Object.prototype.toString({}) // '[object Object]'
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call('a') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(Symbol('a')) // '[object Symbol]'
Object.prototype.toString.call(11n) // '[object BigInt]'
Object.prototype.toString.call(/a/) // '[object RegExp]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call([0, 1, 2]) // '[object Array]'
Object.prototype.toString.call(function() {}) // '[object Function]'
Object.prototype.toString.call(new Error()) // '[object Error]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new Map()) // '[object Map]'
// 实现检测数据类型的通用方法
const getType = (obj) => {
    const type = typeof obj
    if ( type !== 'object') return type
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

getType('1') === 'string' // true
getType(null) === 'null' // true
getType({}) === 'object' // true
getType([]) === 'array' // true