JavaScript 四种数据类型判断方法

JavaScript 属于弱类型(动态类型)语言,不需要提前声明变量类型,运行时自动确定类型。同一个变量可以先后存储多种不同类型的数据:

let a = 42
a = "bar"
a = true
a = {}

ECMAScript 标准一共定义 8 种数据类型:

7 种基本(原始)类型BooleanNullUndefinedNumberBigIntStringSymbol

基本类型又叫简单类型。教学上常说它们放在栈上、按值访问;引擎实现里对象在堆上,部分原始值也会落在堆上。除 nullundefined 外都有对应的包装对象。

1 种引用类型Object

Object 是唯一复杂类型。FunctionArrayRegExpDate 等本质都归属于 Object。引用数据存放在堆内存,变量保存的是指向堆内存的指针,按引用访问。

Object 外,所有原始类型的值本身不可变。例如字符串不能原地修改某个字符,操作只会返回新字符串,原始字符串保持不变。

由于 JavaScript 是弱类型,开发中经常需要判断变量实际的数据类型。语言提供了多种判断手段,但没有任何一种可以覆盖全部场景,每种方案都有各自的适用范围与缺陷。

let objT = {
    str: '',
    num: 1,
    bool: true,
    undef: undefined,
    nul: null,
    symbol: Symbol(),
    bigInt: BigInt(10),

    fun: new Function(),

    arr: [],
    numN: new Number(),
    boolN: new Boolean(),
    strN: new String(),
    date: new Date(),
    reg: new RegExp(),
}

typeof

typeof variable

typeof 运算符返回全小写字符串,代表操作数的类型。

优点

  • 使用简单,语法简洁;
  • 可以正确识别:stringnumberbooleansymbolbigintundefinedfunction
  • 未声明的标识符执行 typeof 不会抛错,返回 'undefined'

缺点

  1. typeof null === 'object'(历史遗留,规范保留)

    typeof null // 'object'
    

    JavaScript 最初实现里,对象用类型标签 0 标记,null 在底层是空指针(多数平台为 0x00),因此返回 'object'。曾经有提案改成 'null',最终被拒绝。

  2. function 以外,其余引用类型(数组、DateRegExp 等)全部返回 'object',无法区分。

    typeof []         // 'object'
    typeof new Date() // 'object'
    typeof /abc/      // 'object'
    
  3. 包装对象实例(new Numbernew Stringnew Boolean)识别为 'object',不是对应的原始类型。

    let str = new String('abc')
    typeof str // 'object'
    
  4. 部分老浏览器正则会识别为 function(历史兼容问题,现代浏览器已修复)。

  5. 特殊宿主对象:document.alltypeof document.all === 'undefined',属于标准的故意例外行为。

  6. 暂时性死区(TDZ)里,对已进入作用域但尚未初始化的 letconst 执行 typeof,会抛出 ReferenceError。未声明的标识符才返回 'undefined'

    for (let k in objT) {
        console.log(typeof objT[k])
    }
    // str → string
    // num → number
    // bool → boolean
    // undef → undefined
    // nul → object
    // symbol → symbol
    // bigInt → bigint
    // fun → function
    // arr → object
    // numN → object
    // boolN → object
    // strN → object
    // date → object
    // reg → object
    

适用场景:判断原始类型(除 null)、判断函数;不适合区分数组、日期、正则、null

instanceof

object instanceof constructor

检测构造函数的 prototype 是否出现在实例对象的原型链上。返回布尔值。

优点

  • 可以区分自定义类的实例;
  • 可以识别部分内置引用类型:ArrayDateRegExpFunction

缺点

  1. 不能判断原始类型,原始值直接返回 falsenull instanceof Objectundefined instanceof Object 同样返回 false,不会抛错。右侧不是对象(或不能用作构造函数)时才会抛错。

    'abc' instanceof String   // false
    123 instanceof Number     // false
    true instanceof Boolean   // false
    null instanceof Object    // false
    undefined instanceof Object // false
    
  2. 普通对象都继承 Object,因此 xxx instanceof Object 常常为 true,无法单靠它精确区分真实类型。Object.create(null) 没有原型,instanceof Objectfalse

    [] instanceof Array  // true
    [] instanceof Object // true
    Object.create(null) instanceof Object // false
    
  3. 原型链可以被人为篡改,判断结果不可靠:修改构造函数 prototype,或者修改对象 [[Prototype]](如 __proto__),会改变结果。对象还可以自定义 Symbol.hasInstance

    function A() {}
    const a = new A()
    a instanceof A // true
    A.prototype = null
    a instanceof A // TypeError:prototype 不是对象
    
  4. 跨 iframe、跨窗口环境失效:不同 iframe 拥有独立的全局构造函数。

    document.body.appendChild(document.createElement('iframe'))
    const xArray = window.frames[0].Array
    const arr = new xArray(1,2,3)
    arr instanceof Array  // false
    arr instanceof xArray // true
    

ES5 专门提供 Array.isArray() 解决数组跨窗口判断的问题。

适用场景:判断自定义类的实例对象;仅用于引用类型,不适合原始类型。

constructor

constructor 是原型上的属性,指向创建该实例的构造函数。判断写法:

obj.constructor === Constructor

优点

  • 原始类型在访问 .constructor 时自动包装为包装对象,可以拿到对应构造函数。

    (10).constructor === Number    // true
    'hello'.constructor === String // true
    true.constructor === Boolean   // true
    
  • 可以识别内置对象实例。

缺点

  1. nullundefined 没有对象,访问 .constructor 直接抛出异常。Object.create(null) 没有 constructor,同样会抛错。

    // objT.nul.constructor → Uncaught TypeError
    
  2. constructor 属性可被人为改写,判断不安全

    function A(){}
    const a = new A()
    a.constructor = Date
    a.constructor === A // false
    
  3. 跨 iframe、跨窗口同样失效,和 instanceof 问题一致。

    document.body.appendChild(document.createElement('iframe'))
    const xArray = window.frames[0].Array
    const arr = new xArray(1,2,3)
    arr.constructor === Array  // false
    arr.constructor === xArray // true
    

适用场景:简单业务,且保证不会篡改 constructor;不推荐做严谨类型校验。

Object.prototype.toString.call()

Object.prototype.toString.call(variable)

该方法读取对象的内置品牌(ES5 对应内部属性 [[Class]];现行规范还看 @@toStringTag),返回格式为 [object Xxx] 的字符串。

ArrayDateRegExp 等内置对象重写了自身的 toString,因此不要直接调用 xxx.toString(),必须使用原型方法再 call 改变 this

优点

  • JavaScript 内置类型(原始类型、内置对象)几乎全部可以精准识别

    Object.prototype.toString.call('')           // "[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())     // "[object Symbol]"
    Object.prototype.toString.call(BigInt(1))    // "[object BigInt]"
    Object.prototype.toString.call([])           // "[object Array]"
    Object.prototype.toString.call({})           // "[object Object]"
    Object.prototype.toString.call(new Date())   // "[object Date]"
    Object.prototype.toString.call(/\d/)         // "[object RegExp]"
    Object.prototype.toString.call(function(){}) // "[object Function]"
    
  • 返回纯字符串,不受跨 iframe 环境影响

  • 内置对象的品牌(数组、日期等)不会因为改原型就变成别的标签。对象仍可以自定义 Symbol.toStringTag,从而改变 toString 的返回值。

缺点

  • 默认无法区分自定义类实例,未设置 Symbol.toStringTag 时统一返回 [object Object]

    class Person {}
    const p = new Person()
    Object.prototype.toString.call(p) // "[object Object]"
    
    class Animal {
      get [Symbol.toStringTag]() { return 'Animal' }
    }
    Object.prototype.toString.call(new Animal()) // "[object Animal]"
    

适用场景:JavaScript 所有内置标准类型(原始、内置引用)的严谨判断;自定义类若未设置 Symbol.toStringTag,不能靠它区分。

什么是内部属性 [[Class]] 与 @@toStringTag

ES5 里,Object.prototype.toString 读取内部属性 [[Class]],开发者无法直接访问。现行规范改为优先使用 @@toStringTagSymbol.toStringTag),内置对象仍有固定品牌。

内置对象的标签和其内置构造函数一一对应;nullundefined 也有特殊结果([object Null][object Undefined])。

用户自定义类的实例,若未定义 Symbol.toStringTag,结果仍是 [object Object],所以默认无法用它区分自定义类。

Object.prototype.toString.call([])         // '[object Array]'
Object.prototype.toString.call(/a/i )      // '[object RegExp]'
Object.prototype.toString.call({})         // '[object Object]'
Object.prototype.toString.call(new Date()) // '[object Date]'

Object.prototype.toString.call("abc")     // '[object String]'
Object.prototype.toString.call(1234)      // '[object Number]'
Object.prototype.toString.call(true)      // '[object Boolean]'

Object.prototype.toString.call(null)      // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'

总结

判断方式 可识别原始类型 识别 null 识别内置引用类型 识别自定义类 风险、缺陷
typeof ✅大部分 ❌(仅 function) null 历史行为、TDZ 抛错,无法区分数组、日期
instanceof 原型可篡改、跨 iframe 失效、不能原始类型
constructor 属性可被改写、跨 iframe 失效
Object.prototype.toString.call 默认无法识别自定义类实例
  • 判断内置标准类型(原始、ArrayDateRegExp 等):优先用 Object.prototype.toString.call(),是内置类型最可靠方案;
  • 判断自定义 class、构造函数实例:使用 instanceof
  • 简单判断原始类型(排除 null)、判断是否为函数:可以用 typeof
  • 不推荐业务中依靠 constructor 做类型校验,属性可被篡改,安全性差。

instanceofconstructor 都受原型链篡改、跨窗口 iframe 环境影响,做公共库、工具函数时要格外小心。

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-09-01 22:19:48

results matching ""

    No results matching ""