JS中获取数据类型的方法,你真的知道吗?不一定吧

今天我们来讨论一下js中获取数据类型的各种方法。


在讨论这个话题之前,我们先来看看js中的数据类型。在最新的 ECMAScript 标准中定义了 8 种数据类型:Boolean,Null,Undefined,Number,BigInt,String,Symbol 和 对象类型。


Boolean表示一个逻辑实体,可以有两个值:true 和 false。Null 类型只有一个值:null。


Undefined 类型一个没有被赋值的变量会有个默认值 undefined。


数字类型,根据 ECMAScript 标准,JavaScript 中只有一种数字类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-(253 -1) 到 253 -1)。除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN 。


BigInt 类型是 JavaScript 中的一个基础的数值类型,可以用任意精度表示整数。使用 BigInt,您可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。BigInt是通过在整数末尾附加 n 或调用构造函数来创建的。


String类型用于表示文本数据。它是一组16位的无符号整数值的“元素”。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为0,下一个是索引1,依此类推。字符串的长度是它的元素的数量。

JavaScript 字符串是不可更改的。这意味着字符串一旦被创建,就不能被修改。但是,可以基于对原始字符串的操作来创建新的字符串。


符号(Symbols)是ECMAScript 第6版新定义的。符号类型是唯一的并且是不可修改的, 并且也可以用来作为Object的key的值(如下). 在某些语言当中也有类似的原子类型(Atoms). 你也可以认为为它们是C里面的枚举类型。


对象类型,在 Javascript 里,对象可以被看作是一组属性的集合。申明的方式一般是 var obj = new Object()。


下面我们来正式看看js中四种判断js数据类型的方法。


1、typeof

typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。看下面的例子:



typeof 可以对JS基础数据类型做出准确的判断,而对于引用类型返回的基本上都是object, 其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的`祖宗`。但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了。


2、instanceof


instanceof 是用来判断 A 是否为 B 的实例对,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。在这里需要特别注意的是:instanceof检测的是原型。


我们再来看几个例子:



我们发现,虽然 instanceof 能够判断出 [] 是Array的实例,但它认为 [] 也是Object的实例,为什么呢?我们来分析一下[]、Array、Object 三者之间的关系: 从instanceof 能够判断出 [].__proto__ 指向 Array.prototype, 而 Array.prototype.__proto__ 又指向了Object.prototype,Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object就形成了如下图所示的一条原型链:



从原型链可以看出,[] 的 __proto__ 直接指向Array.prototype, 间接指向Object.prototype, 所以按照 instanceof 的判断规则,[] 就是Object的实例。当然,类似的new Date()、new Person() 也会形成这样一条原型链,因此,instanceof 只能用来判断两个对象是否属于原型链的关系, 而不能获取对象的具体类型。


3、constructor


当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:



当执行 var f = new F() 时,F被当成了构造函数,f是F的实例对象,此时F原型上的constructor传递到了f上,因此f.constructor == F



可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。同理,JS中的数据类型也遵守这个规则:



细节问题:


null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过typeof来判断。

JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object



为什么变成了Object?prototype被重新赋值的是一个{}, {}是new Object()的字面量,因此new Object()会将Object原型上的constructor传递给{},也就是Object本身。因此,为了规范,在重写对象原型时一般都需要重新给constructor赋值,以保证实例对象的类型不被改写。


4、Object.prototype.toString


toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。



需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。