js类型判断

60 阅读3分钟

判断 js 类型

js 有哪八大类型?(红宝书第四版)

  • 原始类型(Primitive Types)
    1. string
    2. number
    3. boolean
    4. null
    5. undefined
    6. bigInt
    7. symbol
  • object 引用类型(Reference Types)
    • 基本引用类型
      • Date
      • RegExp
      • 原始值包装类型 string,number,boolean 的包装类型
    • 集合引用类型
      • Object
      • Array
      • Map weakMap
      • Set weakSet
      • 定型数组

判断 js 对象有四种方法

  1. typeof

    • 可以判断:typeof 可以准确判断除了 null 之外的 6 种原始类型,以及引用类型的 function。
    • 无法判断:对于 null 返回'object',这是历史遗留的问题 对于除了 function 其他引用类型返回 'object'
    • 为什么 typeof (() => {}) 会返回 function? 这里主要还是要看 ES6 中 typeof 是如何区分函数和对象类型的: 一个对象如果没有实现 [[Call]] 内部方法, 那么它就返回 object 一个对象如果实现了 [[Call]] 内部方法, 那么它就返回 function

  1. Object.prototype.toString.call

    • 原理: 返回一个格式为'[object xxx]'的字符串,其中 xxx 为首字母大写。对于原始类型的包装类型(new Number(1))和原始类型(1)结果相同
    • 可以判断:原始类型和引用类型
    • 注意点:这是一个比较全能的方法,但注意修改 Symbol.toStringTag 可以改变它的返回结果,并且例如 Object.prototype.toString.call(1)会进行包装(但区别于传统的隐式类型转换比如 1+'2' 把 1 转成字符串)
    class MyObject {
      // 可以在构造函数中添加静态方法或属性来定义 @@toStringTag
      static get [Symbol.toStringTag]() {
        return "MyCustomObject";
      }
    }
    const obj = new MyObject();
    console.log(Object.prototype.toString.call(obj)); // 输出: [object MyCustomObject]
    

  1. instanceOf & isPrototype

    • 原理:确定原型链的位置
    • 可以判断:引用值
    • 不能判断:原始类型(例如数字 1 在原型链上没有位置)
    • 代码示例
    function Human(name, age) {
      this.name = name;
      this.age = age;
    }
    console.log(new Human() instanceof Human); //true
    console.log(new Human() instanceof Object); //true
    Object.prototype.isPrototypeOf(new Human()); //true
    
  2. constructor

    • 原理:沿着实例对象.__proto__访问到构造函数的 constructor,判断该对象的 constructor 是否等于构造函数
    • 可以判断:引用数据类型,并且可以直接确定上一级的父类
    • 不能判断:原始数据类型、null、undefined,因为这几个数据没有 constructor
    • 代码示例
    Promise.resolve(1).constructor === Promise; //true,可以判断是Promise的new实例
    Promise.resolve(1) instanceof Object; //true
    Promise.resolve(1) instanceof Promise; //true,无法确定是new Object还是new Promise的
    

实际上 cosntructor 和 instanceof | isPrototype 都是根据原型链来确定类型,但是 constructor 是确定相邻父级的类型

需要研究的有

  • 特殊方法
  • 对象能判断什么,不能判断什么
  • 对象能否判断父类
  • 对象能否判断包装类
  • 是否会进行隐式转换

一个精确判断类型的方法需要先怎么写?

先判断直接父类型 再判断它是否为引用类型的某个子类型(引用类型) 再判断是否为基础类型(基础类型)

对于引用类型 先用 constructor 判断,再用 object.prototype.toString.call,instanceof,isprototype 来判断 剩下原始数据数据类型直接用 typeof 来判断

/**
 * 判断给定值的类型。
 *
 * 支持基本类型(string, number, boolean, null, undefined, symbol, bigint, function)
 * 和通过构造函数或构造函数数组自定义的类型。
 * 如果给定的值不是基本类型且没有提供有效的自定义类型,则使用
 * Object.prototype.toString.call 获取其类型。
 *
 * @param {any} val - 要判断类型的值。
 * @param {(Function|Function[])} [customType] - 可选的自定义类型,可以是单个构造函数或包含构造函数的数组。
 * @returns {string} - 返回值的类型名称。
 */
function judgeType(val, customType) {
  if (
    ["string", "number", "boolean", "null", "undefined", "symbol", "bigint", "function"].includes(
      typeof val,
    )
  ) {
    return typeof val;
  }
  if (val === null) {
    return "null";
  }
  //已经判断完所有基础类型,开始判断引用类型
  if (customType) {
    if (
      customType === val.constructor ||
      (Array.isArray(customType) && customType.includes(val.constructor))
    ) {
      return val.constructor.name;
    }
  }
  //已经用类型判断了相邻层级的类型,再用instanceof判断原型链的可能不相邻的关系
  if (customType) {
    if(Array.isArray(customType)){
        cosnt F = customType.find(F =>  instanceof F);
        return F.name;
    }//todo 判断更具体的类型
    if(val instanceof customType){
        reutrn customType.name
    }
  }
  return Object.prototype.toString.call(val).slice(8, -1);
}