JavaScript中判断变量的类型

数据类型

在 JavaScript 中有 8 种基本的数据类型(7 种原始类型和 1 种引用类型),它们分别是:
原始类型/基本类型
NumberBigIntStringBooleannullundefinedSymbol

引用类型/复杂类型:
Object

typeof 运算符

typeof运算符返回参数的类型。当我们想要分别处理不同类型值的时候,或者想快速进行数据类型检验时,非常有用。

typeof x 的调用会以字符串的形式返回数据类型:
注意:返回的结果是字符串,而且全部都是小写

      console.log(typeof 0); // "number"
      console.log(typeof 10n); // "bigint"
      console.log(typeof "foo"); // "string"
      console.log(typeof true); // "boolean"
      console.log(typeof undefined); // "undefined"
      console.log(typeof Symbol("id")); // "symbol"

      function cb() {
        console.log("hello");
      }
      console.log(typeof cb); //"function"

      console.log(typeof null); //"object"
      console.log(typeof [1, 2, 3]); //"object"
      console.log(typeof { a: "a", n: "b" }); //"object"

      let map = new Map();
      let set = new Set();
      console.log(typeof map); //"object"
      console.log(typeof set); //"object"
      console.log(typeof /[0-9]/); //"object"
      console.log(typeof new Date()); //"object"

根据上面的结果可以发现,下面的六种基本类型以及函数类型,typeof可以正确的区分:

      console.log(typeof 0); // "number"
      console.log(typeof 10n); // "bigint"
      console.log(typeof "foo"); // "string"
      console.log(typeof true); // "boolean"
      console.log(typeof undefined); // "undefined"
      console.log(typeof Symbol("id")); // "symbol"
      
      function cb() {
        console.log("hello");
      }
      console.log(typeof cb); //"function"

但是对于null数组,以及普通对象正则以及mapset等类型,则不能更精细的区分,返回的都是object
其中typeof null 会返回 "object" —— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个 object

instanceof 操作符

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,使用方法如下:

//实例对象  instanceof  构造函数
object instanceof constructor

返回结果为true或者false
如果构造函数的prototype属性在object的原型链上,就返回true,否则返回false

      console.log([1, 2, 3] instanceof Array); //true
      console.log({ a: "a", n: "b" } instanceof Object); //true
      console.log(null instanceof Object); //false
      
      let map = new Map();
      let set = new Set();
      let time = new Date();
      console.log(map instanceof Map); //true
      console.log(set instanceof Set); //true
      console.log(time instanceof Date); //true
      console.log(/[0-9]/ instanceof RegExp); //true

使用instanceof可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型:
在这里插入图片描述

关于instanceof的实现原理,可以参考下面:

      // 左边是对象,右边是构造函数
      // 原理就是判断左边对象的原型链上,有没有构造函数的prototype属性的对象
      function myInstanceOf(left, right) {
        if (typeof left !== "object" || left === null) return false;
        let obj = Object.getPrototypeOf(left);
        while (obj) {
          if (right.prototype === obj) return true;
          obj = Object.getPrototypeOf(obj);
        }
        return false;
      }
更通用的数据类型检测

Object.prototype.toString(),调用该方法,统一返回格式“[object Xxx]”的字符串:

Object.prototype.toString({})       // "[object Object]"
Object.prototype.toString.call({})  // 同上结果,加上call也ok
Object.prototype.toString.call(1)    // "[object Number]"
Object.prototype.toString.call('1')  // "[object String]"
Object.prototype.toString.call(true)  // "[object Boolean]"
Object.prototype.toString.call(function(){})  // "[object Function]"
Object.prototype.toString.call(null)   //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g)    //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([])       //"[object Array]"
Object.prototype.toString.call(document)  //"[object HTMLDocument]"
Object.prototype.toString.call(window)   //"[object Window]"

简单说下我的理解,在js中除了基本数据类型以外,其他的类型都可视为对象类型,当然也可以使用包装类,将基本类型包装成对象,比如字符串字面量在调用toUpperCase()方法时就会自动将其包装为String对象,使得你可以调用字符串方法:

let str = "hello";
console.log(str.toUpperCase()); // 输出 "HELLO"

可以认为所有的对象都继承自Object.prototype对象,那么所有的对象类型共享从Object.prototype继承来的属性和方法,比如toString()方法。

Object.prototype.toString() 返回 “[object Type]”,这里的 Type 是对象的类型。如果对象有
Symbol.toStringTag 属性,其值是一个字符串,则它的值将被用作 Type。许多内置的对象,包括 Map 和Symbol,都有 Symbol.toStringTag。一些早于 ES6 的对象没有
Symbol.toStringTag,但仍然有一个特殊的标签。它们包括(标签与下面给出的类型名相同):
在这里插入图片描述
arguments 对象返回 “[object Arguments]”。其他所有内容,包括用户自定义的类,除非有一个自定义的 Symbol.toStringTag,否则都将返回 "[object Object]"。

Object.prototype.toString()返回的类型是根据对象的Symbol.toStringTag 属性来的,对于用户自定义类,toStringTag默认使用"Object"标签。

class ValidatorClass {}

Object.prototype.toString.call(new ValidatorClass()); // "[object Object]"

看下面的代码:
这里我们自定义类,接着设置自己的自定义标签。然后创建ValidatorClass 类对应的实例对象v,使用Object.prototype.toString()判断类型,可以发现,这个过程是从v实例的构造函数中的Symbol.toStringTag接口中寻找的。

class ValidatorClass {
  get [Symbol.toStringTag]() {
    return "Validator";
  }
}

let v = new ValidatorClass();
console.log(Object.prototype.toString.call(v)); // "[object Validator]"

这也说明,如果是字符串,数字类型,应该是去String,Number构造函数中找对应的接口。

ECMA文档的描述:
该属性值是一个字符串,用来创建对象的默认字符串描述。可以通过内置方法Object.prototype.toString()获取。
在这里插入图片描述

另外关于为什么需要使用call()绑定到目标对象:
这是因为js中的其他对象都继承自Object.prototype,比如String类型,Array类型,都可以调用toString()方法,但是同时他们也重写了toString()方法,和最初的,即来自Object.prototype对象的toString()不一样了,比如Number.prototype.toString(),返回的是表示该数字值的字符串:

      let n = 123;
      console.log(n.toString()); // "123"
      console.log(typeof n.toString()); //string

所以需要使用最初的toString()方法,同时改变其this的指向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值