FumadocsZDecode
JavaScript

类型判断

一、typeof

最常用,但有坑。

typeof 'hello' // 'string'
typeof 123 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof BigInt(1) // 'bigint'
typeof function () {} // 'function'

// 坑:
typeof null // 'object'  ← 历史 bug,永远不会修
typeof [] // 'object'  ← 分不清数组和对象
typeof {} // 'object'

结论:能判断基本类型(除 null),不能细分引用类型。


二、instanceof

原型链判断

原理:沿着对象的 __proto__ 链往上找,看能否找到构造函数的 prototype

[] instanceof Array   // true
[] instanceof Object  // true(原型链最终到 Object.prototype)

// 原型链过程:
// [].__proto__ === Array.prototype      ✓ → true
// [].__proto__.__proto__ === Object.prototype ✓ → 也是 true

手写 instanceof

function myInstanceof(obj, Constructor) {
  let proto = Object.getPrototypeOf(obj)
  while (proto !== null) {
    if (proto === Constructor.prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
  return false
}

myInstanceof([], Array) // true
myInstanceof([], Object) // true

缺点

// 1. 不能判断基本类型
'hello' instanceof String // false(基本类型不是对象)

// 2. 跨 iframe 失效
// iframe 里的数组和主页面的 Array 不是同一个构造函数

三、Object.prototype.toString.call()

最准确

原理:每个对象内部有 [[Class]] 标签,toString 会读取它。

Object.prototype.toString.call('hello') // '[object String]'
Object.prototype.toString.call(123) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(null) // '[object Null]'      ← 能正确识别 null
Object.prototype.toString.call([]) // '[object Array]'     ← 能区分数组
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call(function () {}) // '[object Function]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(/abc/) // '[object RegExp]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new Set()) // '[object Set]'

封装工具函数

function getType(val) {
  return Object.prototype.toString.call(val).slice(8, -1).toLowerCase()
}

getType(null) // 'null'
getType([]) // 'array'
getType(new Map()) // 'map'

为什么要用 call 数组、Date 等重写了自己的 toString,直接调用拿不到 [[Class]],必须借用 Object.prototype 上的那个。


四、Array.isArray()

专门判断数组,跨 iframe 也准

Array.isArray([]) // true
Array.isArray({}) // false
Array.isArray('hello') // false

内部等价于 Object.prototype.toString.call(arg) === '[object Array]'


五、constructor

(123).constructor === Number // true

'hello'.constructor === String // true

[].constructor === Array // true

// 缺点:
null.constructor // 报错
undefined.constructor // 报错
// 原型链被改写后也不准

六、对比总结

方法基本类型引用类型细分null跨 iframe
typeof✅(除 null)❌ 返回 'object'
instanceof
toString.call()
Array.isArray仅数组
constructor❌ 报错

判断基本类型用 typeof,判断引用类型用 instanceof(原型链查找),最准确的是 Object.prototype.toString.call(),判断数组专用 Array.isArray()

On this page