前端系统化学习【JS篇】:(七)进行数据类型检测的几种思路

224 阅读2分钟

前言

  • 细阅此文章大概需要 10分钟\color{red}{10分钟}左右
  • 本篇中讲述了:
      1. 进行数据类型检测的几种思路
        1. typeof
        1. instanceof
        1. constructor
        1. Object.prototype.toString.call()
      1. typeof的详解
      1. instanceof的详解
        1. 手动实现一个instanceof
      1. constructor的详解
      1. Object.prototype.toString.call()的详解
  • 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!

JS当中的数据类型检测

JS中进行数据类型检测的几种思路

    1. typeof[val]: 用来检测数据类型的 【运算符】,【返回的结果是一个字符串】
    1. instanceof: 用来检测当前实例是否属于某个类,【变相用来检测数据类型,看作是对typeof的补充
    1. constructor: 基于构造函数检测数据类型,(也是基于类的方式)
    1. Object.prototype.toString.call():【检测数据类型的最好方法】
  • 除了最后一种,前三种都有局限性

【运算符】typeof[val]

  • 基于typeof检测出来的结果
    1. 首先是一个字符串
    2. 字符串中包含对应的类型
  • 【局限性】
    1. 【对于基本数据类型】:
      • 检测null的数据类型,结果为"object",但实际上为null ,不是一个对象,只是空对象指针
      • typeof null=>'object'
    2. 【对于引用数据类型】:
      • 不能细分出具体为什么类型的对象 ,因为只要是对象数据类型,返回的结果都是"object"
      • typeof 数组/正则/日期/对象 【只要是对象数据类型】=>'object'
    3. 因为typeof检测的结果都是字符串,所以只要两个及以上同时检测,最后结果必然是"string"
//【对于基本数据类型】
    let a = NaN;
    typeof 1;//=>"number"
    typeof a;//=>"number"
    typeof NaN;;//=>"number"
    typeof 'string';//=>"string"
    typeof true;//=>"boolean"
    typeof undefined;//=>"undefined"
    typeof null;//"object"
    typeof typeof undefined;//=>"string"

//【对于引用数据类型】
    typeof {};//=>"object"
    typeof [];//=>"object"
    typeof /^/;//=>"object"
    typeof function(){};//=>"function"
    typeof typeof typeof function(){};//=>"string"
    

【运算符】instanceof

  • 检测当前实例是否属于这个类【变相用来检测数据类型,看作是对typeof的补充
  • 【缺陷】:
    • 不能用来处理基本数据类型(基本数据类型基于构造函数方式创建的实例是可以的)
    • 只要出现在实例的原型链上的类,该实例的检测结果都是true
      • (可以手动更改原型链的指向,这样导致检测结果不一定准确)
    let arr = [],
        reg = /^$/;
        console.log(arr instanceof Array);//=>true
        console.log(arr instanceof Object);//=>true
        console.log(reg instanceof Array);//=>false

        console.log(1 instanceof Number);//=>false
        console.log(new Number(1) instanceof Number);//=>true
        console.log(Symbol() instanceof Symbol);//=>false

        
        function fn(){}
        fn.prototype = Array.prototype;
        let f = new fn;
        console,log(f instanceof Array);//=>true


手动实现一个instanceof【方法重写】

  • 为了兼容低版本浏览器,可以使用Object.getPrototypeOf()方法来获取,实例的原型链指向。
    • ***【Object.getPrototypeOf()】***返回指定对象的原型(即内部[[Prototype]]属性的值)。
    //IE10以下不提供__proto__属性,所以可以使用Object.getPrototypeOf()返回指定对象的原型(即内部[[Prototype]]属性的值)。
/* 
    example:要检测的实例
    classFunc:要检测的类
*/
   function instance_of(example,classFunc){
       //先来获取被检测类的原型,以及被检测实例的原型链
       let calssPrototype = classFunc.prototype,
            exampleProto = Object.getPrototypeOf(example);//先获取实例的原型
            //exampleProto = example.__proto__;
        while(true){
            if(exampleProto === null){
                //说明已经找到Onject.prototype.__proto__了,都没找到,返回false
                return false;
            }
            if(exampleProto === calssPrototype){
                //说明在当前实例的原型链上已经找到所属类的原型了,返回true
                return true;
            }
            //exampleProto = exampleProto.__proto__;
            exampleProto =Object.getPrototypeOf(exampleProto);//循环一次后没找到,就顺着原型链再找原型的原型
        }
       
   }

  • 而instanceof运算符的处理机制实际上是基于Function.prototype上的一个方法Symbol.hasInstance来实现的。而这个方法具体的实现步骤,就和我上面实现的instance_of的流程一致。

    • 在我们执行【console.log(arr instanceof Array);】时,浏览器内部实际上是基于Function.prototype上的一个方法Symbol.hasInstance来检测的。
    • 相当于【console.log(Array【Symbol.hasInstance】(arr));】Array通过原型链找到Function.prototype上的方法Symbol.hasInstance
    • 若要重写Symbol.hasInstance方法,原理与上面instance_of基本一致,只是涉及到类的this指向问题如 Array通过原型链找到Function.prototype上的方法Symbol.hasInstance

constructor: 基于构造函数检测数据类型,(也是基于类的方式)

  • [实例].consturctor === [类]判断某个实例的构造函数是否是指定的类
  • 【缺陷】
    • constructor属性也是可以被修改的,(所以这个结果也不一定准确)
    • 基本数据类型也可以处理
    (1).constructor === Number//=>true
    [].constructor === Object//=>false

Object.prototype.toString.call():【检测数据类型的最好方法】

  • 其他类的原型上的toString都是用来转换字符串的,只有Object原型上的toString是用来检测数据类型的,返回结果'object 所属的类'
    • object.prototype.toString执行,他中的this是谁,就是检测谁的数据类型
    • object.prototype.toString.call(xxx) 若要检测其他类实例,则要改变this指向
      • 相当于({}).toString.call(xxx)
  • 最强大的检测数据类型的方法,(基本上没有弊端)
    object.prototype.toString.call(null)
    object.prototype.toString.call(1)
    object.prototype.toString.call(new Number(1)) 
    object.prototype.toString.call(Symbol())
    object.prototype.toString.call(function(){})
    object.prototype.toString.call(Object)
    object.prototype.toString.call({})
    object.prototype.toString.call([])
    object.prototype.toString.call(/^$/)
    object.prototype.toString.call(new Date())

    function fn(){}
    let f = new fn();

    object.prototype.toString.call(f);//=>'[object object]'