检查数据类型的4种办法

2,595 阅读5分钟

一、typeof

  • tyepof [value] :检测数据类型的运算符

  • 返回值:返回字符串, 字符串中包含了对应的数据类型(例如: “number”、“string”、“boolean”、“undefined”、“object”、“function”、“symbol”、“bigint”)

typeof NaN
"number"
typeof 2
"number"
typeof unll
"undefined"
typeof null
"object"
typeof undefined
"undefined"
typeof {}
"object"
typeof []
"object"
typeof Boolean
"function"
typeof Number
"function"
typeof Object
"function"
typeof function(){}
"function"
typeof '23'
"string"
typeof true
"boolean"
typeof Infinity
"number"
typeof new Number(1)
"object"
  • 优点:使用起来简单,基本数据类型值基本上都可以有效检测,引用数据类型值也可以检测出来

  • 局限性

【1】 NaN / Infinity/ 2 都是数字类型的,检测结果都是“number”;
【2】 typeof null 的结果是“object”;
【3】 Date/Object/RegExp/Number 都是返回function,不能判断具体类型 【4】 {}/[] 都是返回"object",无法基于typeof 区分是普通对象还是数组对象

(这是浏览器的BUG:所有的值在计算中都以二进制编码储存,浏览器中把前三位000的当作对象,而null的二进制前三位是000,所以被识别为对象,但是他不是对象,他是空对象指针,是基本类型值)

  • 应用场景

我们不需要确认其具体数据类型,但要知道是对象类型(任何对象),啥时候会这样用呢?比如跟后台联调,大概数据结构确定,可以这么使用,区分null或者undefined 和 对象

if (typeof x == "object") {         
    //=>null检测结果也会是"object",所以结果是"object"不一定是对象,还可能是null呢
    ...
}
<!--可以这样判断-->
if (x != null && typeof x == "object") {
    // ...
}

二、instanceof

[example] instanceof [class] : 检测某一个实例的原型链上是否市这个类的原型属性

具体详情,参考我的 结合实例对instanceof详解

  • 返回TRUE,不属于返回FALSE
  • 优点:可以弥补 typeof无法细分对象类型的缺点(想检测这个值是否为数组,只需要看他是否为Array类的实例即可)
let arr = [10, 20];

console.log(typeof arr); //=>"object"
console.log(arr instanceof Array); //=>true
console.log(arr instanceof RegExp); //=>false
console.log(arr instanceof Object); //=>true  不管是数组对象还是正则对象,都是Object的实例,检测结果都是TRUE,所以无法基于这个结果判断是否为普通对象 
let a = new String();
console.log(a instanceof String); //true

var b = '2'
console.log(b instanceof String); // false

var b = String('2')
console.log(b instanceof String); //false

var b = {a:1}
console.log(b instanceof Object); // true

instanceof检测的实例必须都是引用数据类型的,它对基本数据类型值操作无效

  • 局限性

【1】 要求检测的实例必须是对象数据类型的
【2】 不能检测出基本数据类型, 构造函数创建的可以检测 【3】 不管是数组对象还是正则对象,都是Object的实例,检测结果都是true,所以无法基于这个结果判断是否为普通对象

// instanceof检测机制:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true
function Fn() {}
Fn.prototype = Object.create(Array.prototype);
let f = new Fn;  //let f = new Fn(); 测试了这样写也可以
console.log(f instanceof Array); //=>true f其实不是数组,因为它连数组的基本结构都是不具备的 

  • 使用场景️

它本身不能完成数据类型检测,只是利用它检测某个实例是否属于这个类的特征来完成数据检测。比如检测具体结构函数名

思考:如果子继承了父,这样可能是检测到父的结构函数,由此可以判断是否能引用某个函数

三、constructor

[example].constructor===[class] :检测实例和类关系的,从而检测数据类型

  • 返回值:属于返回TRUE,不属于返回FALSE
  • 优点:

【1】 实例.constructor一般都等于类.prototype.constructor也就是当前类本身(前提是你的 constructor 并没有被破坏) 【2】 能检测基本数据类型

let arr = [],
    obj = {},
    num = 10;
console.log(arr.constructor === Array); //=>true
console.log(arr.constructor === Object); //=>false
console.log(obj.constructor === Object); //=>true
console.log(num.constructor === Number); //=>true 
  • 局限性:

【1】不能够给当前类的原型进行重定向,否则会造成检测的结果不准确(Object)
【2】不能够给当前实例增加私有属性constructor,也会造成检测的结果不准确(Object)
【3】非常容易被修改,因为JS中的constructor是不被保护的(用户可以自己随便改),这样基于constructor检测的值存在不确定性(但是项目中,基本没有人会改内置类的constructor)

  • 使用场景

它本身不能完成数据类型检测,当没有重定向时候,利用他的实例数据类型检测(不能重定向)

可以用来检测自定义类

四、Object.prototype.toString.call()

Object.prototype.toString.call([value]):检测数据类型

({}).toString.call([value])

  • 返回值:是一个字符串“[Object 当前被检测实例所属的类]”
let class2type = {};
let toString = class2type.toString; //Object.prototype.toString

console.log(toString.call(10)); //"[object Number]"
console.log(toString.call(NaN)); //"[object Number]"
console.log(toString.call("xxx")); //"[object String]"
console.log(toString.call(true)); //"[object Boolean]"
console.log(toString.call(null)); //"[object Null]"
console.log(toString.call(undefined)); //"[object Undefined]"
console.log(toString.call(Symbol())); //"[object Symbol]"
console.log(toString.call(BigInt(10))); //"[object BigInt]"
console.log(toString.call({xxx:'xxx'})); //"[object Object]"
console.log(toString.call([10,20])); //"[object Array]"
console.log(toString.call(/^\d+$/)); //"[object RegExp]"
console.log(toString.call(function(){})); //"[object Function]" 

Object.prototype.toString.call(function A(){}) //"[object Function]"
class B{}
Object.prototype.toString.call(B) //"[object Function]"
Object.prototype.toString.call(class B{}) //"[object Function]"

不能检测出自定义类和函数
  • 优点:

专门用来检测数据类型的方法,基本上不存在局限性的数据类型检测方式 基于他可以有效的检测任何数据类型的值

  • 局限性:

【1】只能检测内置类,不能检测出自定义类和结构函数 【2】只要是自定义类返回的都是‘[Object Function]’

  • 使用场景

此方法是基于JS本身专门进行数据检测的,所以是目前检测数据类型比较好的方法。

内置类都可以用来检测。

function isArray(obj){
   if(!Array.isArray){
        return Object.prototype.toString.call(o)=='[object Array]';
    } else {
        return Array.isArray(obj)
    }
}

总结

综合以上,可以总结出如下使用场景:

  • 检测基本数据类型:typeof

  • 可以用来检测自定义类和结构函数:constructor / instanceof

  • 检查内置类:Object.prototype.toString.call()

  • 实例的原型链:instanceof

参考链接:juejin.cn/post/684490…