本文详细介绍JavaScript中判断数据类型的四种常用方法:typeof、instanceof、Object.prototype.toString.call()以及constructor。文章解析了各方法的底层原理、适用场景及局限性,并通过代码示例对比了它们在处理基本数据类型和复杂对象时的表现,帮助开发者根据实际需求选择最合适的类型判断方式。
JavaScript内置数据类型概述
JavaScript语言定义了八种内置数据类型,除了对象(object)之外,其余七种通常被归类为基本类型(primitive types)。
- 空值(null)
- 未定义(undefined)
- 布尔值(boolean)
- 数字(number)
- 字符串(string)
- 对象(object)
- 符号(symbol,ES6标准新增)
- 大整数(BigInt,ES2020标准引入)
Symbol类型是ES6规范中引入的一种原始数据类型,旨在生成独一无二的值,常用于防止属性名冲突。
BigInt类型是ES2020标准引入的新数据类型,旨在解决JavaScript中数字精度问题。由于JavaScript所有数字均以64位浮点数格式存储,其整数安全范围仅为53个二进制位;超过此范围的整数无法精确表示,BigInt提供了对此类大整数的支持。
typeof运算符原理与局限
一、typeof运算符
typeof是一个操作符而非函数,其右侧需跟随一个一元表达式,并返回该表达式的类型字符串。返回结果始终为全小写的类型名称,涵盖以下八种:number、boolean、symbol、string、object、undefined、function以及bigint。
typeof的实现原理基于底层二进制表示:在JavaScript引擎内部,对象的前三位二进制位用于存储类型信息。
- 000:代表对象类型
- 010:代表浮点数类型
- 100:代表字符串类型
- 110:代表布尔类型
- 1:代表整数类型
console.log(typeof undefined) // undefind
console.log(typeof null) // object
console.log(typeof true) // boolean
console.log(typeof 43) // number
console.log(typeof '21') // string
console.log(typeof {a:1}) // object
console.log(typeof Symbol()) // symbol
console.log(typeof 123n) // bigint
function a() {}
console.log(typeof a) // function
var date = new Date()
var error = new Error()
console.log(typeof date) // object
console.log(typeof error) // object
instanceof运算符与原型链检测
二、instanceof运算符
instance用于判断对象A是否为构造函数B的实例,语法为A instanceof B。若A是B的实例,则返回true,否则返回false。需要注意的是,instanceof检测的是原型链关系。
通俗而言,instanceof用于判断一个对象是否属于某个构造函数的实例。该运算符能准确识别复杂数据类型(如Array、Date等),但无法正确判断基本数据类型(如Number、String、Boolean的基本包装类型除外)。
console.log(12 instanceof Number) // false
console.log('22' instanceof String) // false
console.log(true instanceof Boolean) // false
console.log(null instanceof Object) // false
console.log(undefined instanceof Object) // false
console.log([] instanceof Array) // true
console.log({a: 1} instanceof Object) // true
console.log(json instanceof Object) // true
function a() {}
console.log(a instanceof Function) // true
console.log(new Date() instanceof Date) //true
console.log(reg instanceof RegExp) //true
console.log(error instanceof Error) // true
Object.prototype.toString.call()精确判断
三、Object.prototype.toString.call()方法
toString()是Object的原型方法,调用该方法默认返回当前对象的[[Class]]内部属性。该属性的格式固定为[object Xxx],其中Xxx即为对象的具体类型名称。
对于Object类型的对象,直接调用toString()即可返回[object Object]。而对于其他类型的对象,必须通过call或apply方法借用Object.prototype.toString,才能获取正确的类型信息。
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call(1n)) // [object BigInt]
console.log(Object.prototype.toString.call('123')) // [object String]
console.log(Object.prototype.toString.call(true)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(function a() {})) // [object Function]
console.log(Object.prototype.toString.call(Symbol())) // [object Symbol]
console.log(Object.prototype.toString.call(Math)) // [object Math]
console.log(Object.prototype.toString.call(JSON)) // [object JSON]
console.log(Object.prototype.toString.call(new Date())) // [object Date]
console.log(Object.prototype.toString.call(new RegExp())) // [object RegExp]
console.log(Object.prototype.toString.call(new Error)) // [object Error]
console.log(Object.prototype.toString.call(window) // [object Window]
console.log(Object.prototype.toString.call(document) // [object HTMLDocument]
利用该方法,我们可以封装一个通用的isType函数,用于精确判断数据类型。
let isType = (type, obj) => {
return Object.prototype.toString.call(obj) === `[object ${type}]`
}
console.log(isType('Number', 12)) // true
console.log(isType('Number', '12')) // false
或者采用另一种封装方式:
let type = function(o) {
let s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
console.log(type(12)) // number
console.log(type('12')) // string
console.log(type({})) // object
console.log(type([])) // array
constructor属性与原型关联
四、constructor属性
constructor属性用于标识实例对象是由哪个构造函数创建的。
constructor属性体现了原型对象与构造函数之间的关联。若修改了原型对象,通常需同步修改constructor属性,以避免引用错误。因此,在重写原型对象时,建议同时修正constructor的指向。
console.log('22'.constructor === String) // true
console.log(true.constructor === Boolean) // true
console.log([].constructor === Array) // true
console.log(document.constructor === HTMLDocument) // true
console.log(window.constructor === Window) // true
console.log(new Number(22).constructor === Number) // true
console.log(new Function().constructor === Function) // true
console.log((new Date()).constructor === Date) // true
console.log(new RegExp().constructor === RegExp) // true
console.log(new Error().constructor === Error) // true
需要注意以下限制:
1. null和undefined是无效对象,不存在constructor属性,因此需通过其他方式判断这两种类型。
2. 函数的constructor属性不稳定,尤其在自定义对象场景中。当开发者重写prototype后,原有的constructor引用会丢失,默认指向Object。
以上即为JavaScript中判断数据类型的四种主要方法。如需了解更多JavaScript类型判断相关内容,请参阅本站其他文章。