zoukankan      html  css  js  c++  java
  • js判断数据类型的四种方法

    在 ECMAScript 规范中,共定义了 7 种数据类型,分为基本类型和引用类型两大类。

      其中:

      基本类型:String、Number、Boolean、Symbol、Undefined、Null

      引用类型:Object

      基本类型也称为简单类型,由于其占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈(stack)中,即按值访问。

      引用类型也称为复杂类型,由于其值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此,其值存储在堆(heap)中,而存储在变量处的值,是一个指针,指向存储对象的内存处,即按址访问。引用类型除 Object 外,还包括 Function 、Array、RegExp、Date 等等

    1. typeof

      typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。

      返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:string、number、boolean、symbol、undefined、object、function 等。

    typeof 'a'; // string 有效
    typeof 1; // number 有效
    typeof true; //boolean 有效
    typeof Symbol(); // symbol 有效
    typeof undefined; //undefined 有效
    typeof new Function(); // function 有效
    typeof null; //object 无效
    typeof [1] ; //object 无效
    typeof new RegExp(); //object 无效
    typeof new Date(); //object 无效
    
    1. instanceof
      instanceof用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。instanceof检测的是原型,内部机制是通过判断对象的原型链中是否有类型的原型。
    {} instanceof Object; //true
    [] instanceof Array;  //true
    [] instanceof Object; //true
    "123" instanceof String; //false
    new String(123) instanceof String; //true
    

    虽然 instanceof 能够判断出 [ ] 是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 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

      针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。

    if (Array.isArray(value)){
       //对数组执行某些操作
    }
    

      Array.isArray() 本质上检测的是对象的 [[Class]] 值。

      [[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx],Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。

    我们可以用下面的代码实现instanceof。

    function instance(left,right){
        let prototype = right.prototype;  //获取类型的原型
        let proto = left.__proto__;       //获取对象的原型
        while(true){    //循环判断对象的原型是否等于类型的原型,直到对象原型为null,因为原型链最终为null
           if (proto === null || proto === undefined){
               return false;
           }
           if (proto === prototype){
               return true;
           }
           proto = proto.__proto__;
         }
    }
    console.log(instance({},Object)); //true
    console.log(instance([],Number)); //false
    
    1. constructor
      当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。如下图所示:

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

    可以看出,F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。这样做的意义是,让新对象在诞生以后,就具有可追溯的数据类型。
    概括:当一个函数F被定义时,JS引擎会为F添加prototype原型,然后在prototype上添加一个constructor属性,并让其指向F的引用,F利用原型对象的constructor属性引用了自身,当F作为构造函数创建对象时,原型上的constructor属性被遗传到了新创建的对象上,从原型链角度讲,构造函数F就是新对象的类型。这样做的意义是,让对象诞生以后,就具有可追溯的数据类型。

    总结:
      1. null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
      2. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

    1. Object.prototype.toString()
      toString()是Object的原型方法,调用该方法,默认返回当前对象的[[Class]]。这是一个内部属性,其格式为[object Xxx],其中Xxx就是对象的类型。
      对于Object对象,直接调用toString()就能返回[object Object],而对于其他对象,则需要通过call、apply来调用才能返回正确的类型信息。
    Object.prototype.toString.call('') ;   // [object String]
    Object.prototype.toString.call(1) ;    // [object Number]
    Object.prototype.toString.call(true) ; // [object Boolean]
    Object.prototype.toString.call(Symbol()); //[object Symbol]
    Object.prototype.toString.call(undefined) ; // [object Undefined]
    Object.prototype.toString.call(null) ; // [object Null]
    Object.prototype.toString.call(new Function()) ; // [object Function]
    Object.prototype.toString.call(new Date()) ; // [object Date]
    Object.prototype.toString.call([]) ; // [object Array]
    Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
    Object.prototype.toString.call(new Error()) ; // [object Error]
    Object.prototype.toString.call(document) ; // [object HTMLDocument]
    Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
    

    封装一个准确判断数据类型的函数

    function getType(obj){
      let type  = typeof obj;
      if(type != "object"){
        return type;
      }
      return Object.prototype.toString.call(obj).replace(/^[object (S+)]$/, '$1');
    }
    

    参考地址:
    https://www.cnblogs.com/crackedlove/p/10331317.html
    https://www.cnblogs.com/gg-qq/p/10815109.html

    砥砺前行
  • 相关阅读:
    Android中的内部类引起的内存泄露
    Android的消息机制: Message/MessageQueue/Handler/Looper
    ArrayList/Vector的原理、线程安全和迭代Fail-Fast
    JVM中的Stack和Frame
    JVM中的垃圾收集算法和Heap分区简记
    无锁编程以及CAS
    简述Java内存模型的由来、概念及语义
    MQTT协议简记
    RabbitMQ的工作队列和路由
    RabbitMQ 入门
  • 原文地址:https://www.cnblogs.com/lhongsen/p/14847471.html
Copyright © 2011-2022 走看看