This site is no longer maintained. Please visit zdecode.vercel.app
ZDecode
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()