JavaScript中不得不学的类型检测(上)

719 阅读3分钟

一.基本数据类型的检测用typeof运算符

1.1 typeof运算符

对,你没听错,typeof是个运算符,准确的来说是个一元运算符,不是方法,不是方法,不是方法,重要的事情说三遍。 一般我们看见的typeof的用法是这样的

console.log(typeof('heihei')) // string

但其实它还可以这样用

console.log(typeof 'heiheihei') // string

惊不惊喜,意不意外,

来,兄弟姐妹们,告诉我,基本数据类型包括那些?

JavaScript有八种内置类型

  • 空值(null)
  • 未定义(undefined)
  • 布尔值(boolean)
  • 数字(number)
  • 字符串(string)
  • 对象 (object)
  • 符号(symbol, ES6中新增)
  • 大整数(BigInt, ES2020 引入)

1.2 如何使用

console.log(typeof(1)); // "number"
console.log(typeof("123")); // "string"
console.log(typeof("true")); // "boolean"
console.log(typeof(undefined)); // "undefined"
console.log(typeof("null")); // "object"
console.log(typeof({ a: 1 })); // "object"
console.log(typeof Symbol()) ;// "Symbol"
console.log(typeof (123n)); //"bigint"
console.log(function a() {}); //"function"

有些细心的同学可能会发现,除了以上提到的8种基本数据类型,我在代码中还举了一个检测function的例子,这是因为function虽然也是object的一种,但是它却可以被typeof检测出来,而像Array,Date,RegExp这些数据类型typeof没办法做到一个很好的区分,而关于null为什么也会被检测成"object",我会在下篇文章中讲到。

二.引用数据类型的检测用toString方法

2.1 强大的Object.prototype.toString

typeof只能用于检测基本数据类型与function类型(再说一遍,基本数据类型只有6种,function属于object的一种),那么object中其他的数据类型怎么办呢?

答案就是通过Object.prototype.toString这个方法,有些同学可能对这个方法不是很了解,其实简单来讲就是这个方法会返回一个由"[object" 和 class "]"组成的字符串。class在这里指的就是类型。

直接看代码吧。

console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]

toString的用法一目了然,对吧,兄弟们。

通过Object.prototype.toString这个方法,我们可以至少识别以下这么多种数据类型。

2.2 如何使用

// 以下是11种:
var number = 1;          // [object Number]
var string = '123';      // [object String]
var boolean = true;      // [object Boolean]
var und = undefined;     // [object Undefined]
var nul = null;          // [object Null]
var obj = {a: 1}         // [object Object]
var array = [1, 2, 3];   // [object Array]
var date = new Date();   // [object Date]
var error = new Error(); // [object Error]
var reg = /a/g;          // [object RegExp]
var func = function a(){}; // [object Function]

function checkType() {
for (var i = 0; i < arguments.length; i++) {
console.log(Object.prototype.toString.call(arguments[i]))
 }
}

checkType(number, string, boolean, und, nul, obj, array, date, error, reg, func)

除此之外,还有这Math,JSON这两种不常被检测的数据类型

console.log(Object.prototype.toString.call(Math)); // [object Math]
console.log(Object.prototype.toString.call(JSON)); // [object JSON]

甚至还可以检测出Arguments

function a() {
    console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
}
a();

兄弟们,这玩意是不是非常的强大。

看着这等尤物,不会有人没有对其封装的想法吧,不会吧,直接返回[object Number] [object String]这样的类型的结果也太丑了吧。

我们给它整整容,利用这个方法封装一个可以返回我们熟系的number string等类似的结果。

2.3 给Object.prototype.toString整整容

兄弟们,封装前,我们得明白,封装的意义是什么,我们封装为的是让它不返回类似[object Error]这种检测结果,而是返回类似error这种检测结果。

知道了这一点,我们开始有针对性的对这个方法进行封装吧。

三. 整容过程

3.1 文字叙述

首先我们需要一个对象,来存放上述这两种返回结果的映射关系,举个例子,我要要让返回的[object Error]变成error这不得让它们一一对应起来嘛,大家可以理解为小时候数学老师经常教的连一连那种题目。

其次,我们需要将需要检测的数据类型的字符串全部放进数组中。

再次遍历数组, 使得被包装后的数组元素变成这样[object Number],然后将它变成对象的属性值,与其对应的是小写的不经过包装的数组元素 number变成对象的属性值。这样我们就建立了这两者的对应关系。

3.2 代码解释

const type = (obj) => {
  let typeList = {};  // 1. 创建空对象用于存放映射关系
  "Number Boolean String Null Undefined Array Function Object RegExp Date Error"
    .split(" ")  // 2. 将需要检测的数据类型的字符串全部放进数组中
    .map((item) => {
      return (typeList[`[object ${item}]`] = item.toLowerCase());
    }); //3.建立`[object Number]`与 `"number"`这两种返回结果的映射关系
  return typeof obj === "object" || typeof obj === "function"
    ? typeList[Object.prototype.toString.call(obj)] || "object"
}; // 4.如果检测的是已被封装的引用类型,则返回typeList中对应的属性值即可,如果是未被封装的,则直接放回object。

至此,一个可以用于检测引用类型的相对完善的类型检测函数就诞生了,当然,我是个菜鸟,这是我的个人之言,勿喷。

四.将typeof与Object.prototype.toString结合一下

将这个函数与之前的typeof结合一下,如果是基本数据类型就用typeof检测,如果是应用数据类型就用toString方法。

const type = (obj) => {
  let typeList = {};
  "Number Boolean String Null Undefined Array Function Object RegExp Date Error"
    .split(" ")
    .map((item) => {
      return (typeList[`[object ${item}]`] = item.toLowerCase());
    });
  if (typeof obj == "null") {
    return obj + "";
  }
  return typeof obj === "object" || typeof obj === "function"
    ? typeList[Object.prototype.toString.call(obj)] || "object"
    : typeof obj;
};

至此,我想常规的类型检测应该用这个函数应该是没问题了。

五.拓展与延伸

这篇文章也就先到这里了,当然这只是类型检测的上篇,因为还有一些问题我们并没有解决.

5.1还存在的问题

第一个问题: 是上面封装的方法其实还是有一些类型无法检测,比如window,plainObject等,但是这些类型对于像我这样的新人来说并不常见,因此我也就点到为止了。

第二个问题: 以上两种方法主要用来检测一个对象本身的类型,如果我们想要判断一个对象是不是另外一个对象的实例或者说当我们,想要判断一个实例是否是其父类型或者祖先类型的实例时得使用instanceof。

5.2 之后的计划

在下篇文章中,我会介绍一下instanceof的用法,然后谈一谈typeof与instanceof的原理。

谢谢大家看到这里。

还有就是,这篇文章的灵感来说于这篇文章,作者的其他文章也很棒,希望大家有时间可以看看。

JavaScript专题之类型判断(上)