JS中数据类型检测四种方式的优缺点

对于数据类型检测笔者以前写过一篇typeofJS中数据类型检测方法——typeof,今天就整理下JS中数据类型检测的四种方式的区别:javascript

  • tyepof [value] :检测数据类型的运算符
  • [example] instanceof [class] : 检测某一个实例是否属于这个类
  • [example].constructor===[class] :检测实例和类关系的,从而检测数据类型
  • Object.prototype.toString.call([value]):检测数据类型

1、typeof

  • 一、定义:用来检测数据类型的运算符
  • 二、语法:tyepof [value]
  • 三、返回值:
    • typeof 检测的结果首先是一个字符串;
    • 字符串中包含了对应的数据类型(例如: “number”“string”“boolean”“undefined”“object”“function”“symbol”“bigint”

  • 四、优势:使用起来简单,基本数据类型值基本上均可以有效检测,引用数据类型值也能够检测出来
  • 五、局限性(特殊值)
    • 1)、NaN / Infinity 都是数字类型的,检测结果都是“number”;
    • 2)、typeof null 的结果是“object”;
      • (这是浏览器的BUG:全部的值在计算中都以二进制编码储存,浏览器中把前三位000的看成对象,而null的二进制前三位是000,因此被识别为对象,可是他不是对象,他是空对象指针,是基本类型值)
    • 3)、typeof 普通对象/数组对象/正则对象..., 结果都是“object”,这样就没法基于typeof 区分是普通对象仍是数组对象``...等了
  • 六、应用场景
  • 已知有一个变量x,可是咱们没法确认其数据类型,咱们须要有一个判断操做:当x的类型是对象的时候(什么对象均可以),则处理对应的事情
if (typeof x == "object") {         
    //=>null检测结果也会是"object",因此结果是"object"不必定是对象,还多是null呢
    ...
}
复制代码

能够用👇的条件进行判断java

if (x != null && typeof x == "object") {
    // ...
}
复制代码
  • 七、练习题

console.log(typeof []); //=>"object"面试

console.log(typeof typeof typeof []); //=>"string"数组

需注意:浏览器

因为`typeof`返回的结果永远是一个字符串(字符串中包含了对应的类型),因此连续出现`两个及两个以上typeof检测`的时候,最后结果都是` "string"` bash

2、instanceof

  • 一、定义:用来检测某个实例是否属于这个类
    • 当前类的原型只要出如今了实例的原型链上就返回true
  • 二、语法:实例 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,因此没法基于这个结果判断是否为普通对象 
复制代码
  • 五、局限性:
    • 1)、要求检测的实例必须是对象数据类型的
    • 2)、基本数据类型的实例是没法基于它检测出来的
      • 字面量方式建立的不能检测
      • 构造函数建立的就能够检测
    • 3)、不论是数组对象韩式正则对象,都是 Object 的实例,检测结果都是 TRUE ,因此没法基于这个结果判断是否为普通对象
// instanceof检测的实例必须都是引用数据类型的,它对基本数据类型值操做无效
console.log(10 instanceof Number); //=>false
console.log(new Number(10) instanceof Number); //=>true

// instanceof检测机制:验证当前类的原型prototype是否会出如今实例的原型链__proto__上,只要在它的原型链上,则结果都为TRUE
function Fn() {}
Fn.prototype = Object.create(Array.prototype);
let f = new Fn;
console.log(f instanceof Array); //=>true f其实不是数组,由于它连数组的基本结构都是不具有的 
复制代码

注意️:它自己不能完成数据类型检测,只是利用它(检测某个实例属否属于这个类的)特征来完成数据检测函数

3、constructor

  • 一、定义:判断当前的实例的constructor的属性值是否是预估的类(利用他的实例数据类型检测)
  • 二、语法:实例.constructor === 类
  • 三、返回值:属于返回TRUE,不属于返回FALSE
  • 四、优势:
    • 实例.constructor 通常都等于 类.prototype.constructor 也就是当前类自己(前提是你的 constructor 并无被破坏)
    • 能检测基本数据类型
  • 五、局限性:
    • 1)、不可以给当前类的原型进行重定向,会形成检测的结果不许确(Object)
    • 2)、不可以给当前实例增长私有属性constructor,也会形成检测的结果不许确(Object)
    • 3)、很是容易被修改,由于JS中的constructor是不被保护的(用户能够本身随便改),这样基于constructor检测的值存在不肯定性(可是项目中,基本没有人会改内置类的constructor
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 
复制代码

注意:它自己不能完成数据类型检测,利用他的实例数据类型检测(不能重定向)post

4、Object.prototype.toString.call()

这个方法在Object的原型上ui

  • 一、定义:找到Object.prototype上的toString方法,让toString方法执行,而且基于call让方法中的this指向检测的数据值,这样就能够实现数据类型检测了
  • 二、原理:
    • 1.每一种数据类型的构造函数的原型上都有toString方法;
    • 2.除了Object.prototype上的toString是用来返回当前实例所属类的信息(检测数据类型的),其他的都是转换为字符串的
    • 3.对象实例.toString()toString方法中的THIS是对象实例,也就是检测它的数据类型,也就是THIS是谁,就是检测谁的数据类型
    • 4.Object.prototype.toString.call([value]) 因此咱们是把toString执行,基于call改变this为要检测的数据值
  • 三、使用方法:
    • Object.prototype.toString.call(被检测的实例)
    • ({}).toString.call(被检测的实例)
Object.prototype.toString.call(10)
({}).toString.call(10)
({}).toString===Object.prototype.toString
复制代码
  • 四、返回值:
    • 是一个字符串“[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]" 
复制代码
  • 五、优势:
    • 专门用来检测数据类型的方法,基本上不存在局限性的数据类型检测方式
    • 基于他能够有效的检测任何数据类型的值
  • 六、局限性:
    • 1)、只能检测内置类,不能检测自定义类
    • 2)、只要是自定义类返回的都是‘[Object Object]’

注意:此方法是基于JS自己专门进行数据检测的,因此是目前检测数据类型比较好的方法this

思惟导图

JQ 中基于数据类型检测的方法——本身封装

JQ 中对于数据类型检测,笔者理解的主要仍是利用Object.prototype.toString.call()方法,也能够说是Object.prototype.toString.call()能够完成数据类型检测的原理解析;

function toType(obj) {
    let class2type = {},
        toString = class2type.toString, //=>Object.prototype.toString 检测数据类型
        arr = "Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ");
    arr.forEach(item => {
        class2type["[object " + item + "]"] = item.toLowerCase();
    })
    /* console.log(class2type); { [object Boolean]: "boolean", [object Number]: "number", [object String]: "string" ...... } */

    //传递给个人是null/undefined,直接返回 "null"/"undefined"
    if (obj == null) {
        return "" + obj;
    }

    // typeof obj === "object" || typeof obj === "function" =>引用数据类型
    // => 若是是基本数据类型值,检测数据类型使用typeof就能够
    // => 若是是引用数据类型值,则基于对象的toString就能够
    // => toString.call(obj) 检测当前值的数据类型 "[object Xxx]"
    // => class2type["[object Xxx]"] 当上一步生成的对象中,基于对应的属性名,找到属性值(所属的数据类型),若是没有则返回 "object"
    return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj;
};

console.log(toType(1)); //=>"number"
console.log(toType(NaN)); //=>"number"
console.log(toType([])); //=>"array"
console.log(toType(/^\d+$/)); //=>"regexp"
console.log(toType({})); //=>"object"
console.log(toType(null)); //=>"object"
console.log(toType()); //=>"object"
复制代码

上面咱们已经阐述完数据类型检测四种方式的优缺点;

咱们提到了在Object原型上的toString方法,这里顺便提一下另一个在Object原型上的valueOf方法

散知识:Object原型上的valueOf方法

每一种数据类型的构造函数的原型上都有toString方法;

每一种基本数据类型的构造函数的原型上都有valueOf方法;Object.prototype的原型上也有valueOf方法;

基本数据类型中:

  • valueOf:是获取原始值 [[PrimitiveValue]]
  • toString:是将原始值转换为字符串
let num1 = 10,
    num2 = new Number(10);

console.log(num1);//=> 10
console.log(num1.valueOf());//=> 10
console.log(num1.toString());//=> "10"

console.log(num2);//=>Number {10}
console.log(num2.valueOf());//=> 10
console.log(num2.toString());//=> "10"
复制代码

引用数据类型中

  • 数组中:

上面说过只有基本数据类型的构造函数的原型上都有valueOf方法;Object.prototype的原型上也有valueOf方法;

因此数组的构造函数的原型上是没有这个方法的,咱们用数组调取valueOf方法,其实是经过数组的原型链查找到Object.prototype的原型上,调取valueOf的方法;

let arr = [10, 20],
    obj = {
        xxx: 'xxx'
    };
    
console.log(arr);
console.log(arr.valueOf()); //=>调取的是 Object.prototype.valueOf 原始值
console.log(arr.toString()); //=>调取的是 Array.prototype.toString 转换字符串 

console.log(obj);
console.log(obj.valueOf()); //=>调取的是 Object.prototype.valueOf 原始值
console.log(obj.toString()); //=>调取的是 Object.prototype.toString 检测数据类型 
复制代码

应用——隐性转换

let num1 = 10,
    num2 = new Number(10);
let arr = [10, 20],
    obj = {
        xxx: 'xxx'
    };
复制代码

例如:ALERT会把全部要输出的值转换为字符串(隐性转换),像这种隐性转换为字符串的还有不少,例如:字符串拼接、把对象转换为数字(也是先转换为字符串)...

  • =>num1.valueOf() 先获取原始值
  • =>[[PrimitiveValue]].toString() 把获取的原始值转换为字符串
  • 利用这个机制咱们能够作出一道面试题

面试题:a == 1 && a == 2 && a == 3

原题以下:

//=> 下面代码a在什么值状况下会输出1

var a = ?;
if (a == 1 && a == 2 && a == 3) {
    console.log(1);
}
复制代码

其中一种解题思路是能够利用valueOf获取原始值

var a = {
    n : 0,
    valueOf(){
        return ++this.n;
    }
};
if (a == 1 && a == 2 && a == 3) {
    console.log(1);
}
复制代码

相关文章
相关标签/搜索