本文深入解析JavaScript中获取变量数据类型的三种常用方法:typeof、instanceof以及Object.prototype.toString。重点分析各方法的局限性,如typeof无法区分null与对象,instanceof对基本类型无效,并详细演示如何利用Object.prototype.toString实现一个能准确识别所有内置类型的getType函数,提供完整代码与性能对比。
JavaScript属于弱类型或动态类型语言,在声明变量时无需预先指定类型,且同一变量在生命周期内可被赋予不同类型的值,其具体类型在运行期确定。对于开发者而言,虽然无需频繁定义类型,但在复杂项目或维护他人代码时,若发现变量被反复赋予不同类型,调试难度将显著增加。类型不确定可能导致运行时错误:xxx is not a function,其根本原因在于不同数据类型拥有不同的方法集。若某些方法通用则不报错,但若调用特有或存在差异的方法而不进行类型判断,极易在生产环境中引发Bug。因此,准确获取变量类型至关重要。
判断类型常用的方法
typeof
JavaScript内置运算符,返回表示操作数类型的字符串。它适用于number、string、boolean等值类型,function函数,以及object等引用类型。然而,当用于判断对象类型时,它仅返回object,无法进一步区分具体的对象子类型(如数组、正则等)。
console.log(typeof ''); // string
console.log(typeof (() => {})); // function
console.log(typeof Symbol()); // symbol
console.log(typeof undefined); // undefined
console.log(typeof null); // object
console.log(typeof []); // object
console.log(typeof {}); // object
通过观察可知,数组和对象使用typeof均返回object,无法进行细分。值得注意的是,typeof null的结果也是object。这源于JavaScript早期实现机制:值由类型标签和实际数据组成。对象类型的标签为0,而null表示空值,在多数平台下其值为0x00,因此对象和null拥有相同的类型标签。尽管标签相同,null并不具备正常对象的属性和方法。
instanceof
instanceof同样是JavaScript内置运算符,用于检测两个参数之间是否存在联系。其原理是检查构造函数的prototype属性是否出现在实例对象的原型链中。若存在则返回true,否则返回false。
console.log({} instanceof Array); // false
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log([] instanceof Object); // true
instanceof用于判断两个参数,并不直接返回变量类型,需通过枚举方式间接获取。
toString
每个对象都拥有toString()方法,因为所有对象均继承自Object。许多人认为toString()仅用于返回字符串,并无特殊之处,这是因为常见的toString()大多已被重写。实际上,原始的toString()可用于获取对象的精确类型。
举例说明:
const o = new Object();
o.toString(); // [object Object]
const arr = [1,2,3]; // 数组也会继承自Object
arr.toString(); // '1,2,3',因为数组重写了toString
每个对象均可通过Object.prototype.toString.call(thisArg)或Object.prototype.toString.apply(thisArg)来获取其类型:
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call(() => {})); // [object Function]
每次调用JavaScript内置API较为繁琐,因此我们封装一个名为getType的函数。
封装一个获取类型的函数
typeof + instanceof(不推荐)
前文提到typeof可获取部分数据类型,instanceof可通过枚举表示剩余类型,两者结合是否即可实现getType?
function getType(obj) {
if (typeof obj === 'object') {
if (obj instanceof Array) return 'array';
if (obj instanceof Map) return 'map';
if (obj instanceof Set) return 'set';
if (obj instanceof RegExp) return 'regexp';
// 尽可能枚举类型,最后兜底返回object
return 'object'
}
return (typeof obj);
}
console.log(getType(null)); // object
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // object
console.log(getType(new WeakSet())); // object
使用此方式虽能实现getType,但存在明显弊端:
- 由于依赖枚举,代码长度可能变得冗长。
- 枚举的数据类型可能有限,无法覆盖所有常用数据类型。
- 若需新增类型支持,必须修改现有代码。
toString
关于toString()的用法前文已述,我们据此实现一个getType。
function getType(obj) {
const originType = Object.prototype.toString.call(obj); // 获取类型。返回格式为[object type],需要提取出type的值
const firstSpaceIndex = originType.indexOf(' '); // 找到第一个空格所在下标
const type = originType.slice(firstSpaceIndex + 1, -1); // 跳过空格,截取到倒数第一个字符(不含最后一个)
return type.toLowerCase(); // 转换为小写
}
console.log(getType(null)); // null
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // weakmap
console.log(getType(new WeakSet())); // weakset
实现getType亦可借助数组操作方式:
function getType(obj) {
const originType = Object.prototype.toString.call(obj); // 获取类型。返回格式为[object type],需要提取出type的值
const tmpType = originType.split(' ')[1]; // 根据空格分割,并取第二个
const type = tmpType.slice(0, -1); // 从0截取到倒数第一个字符(不含最后一个)
return type.toLowerCase(); // 转换为小写
}
console.log(getType(null)); // null
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // weakmap
console.log(getType(new WeakSet())); // weakset
两种实现结果一致,基本达成获取所有数据类型的目标。区别在于,操作数组比直接处理字符串消耗更多性能。
总结
实现获取变量类型的方法并不复杂,核心在于使用内置API:Object.prototype.toString()。有时因过于简单而被忽视,但越是简单的方式,往往越有效。
以上就是详解JavaScript如何准确获取任意变量的数据类型的详细内容,更多关于JavaScript获取变量数据类型的资料请关注本站其它相关文章!