“一网打尽”js中的数据类型检测

152 阅读6分钟

js中的数据类型

我们都知道js中的数据类型分为基本数据类型和复杂数据类型,其中基本数据类型包括7种:String、Number、Boolean、Null、Undefined、Symbol(ES6新增)、BigInt(ECMAScript 2020 新增)。复杂数据类型统称为Object,其中子类型包括:Function、Date、RegExp、Error、Math、Array、普通Object。

你知道怎么去检测它们吗?

有经验的同学肯定会说,检测数据类型一共有四种:

  1. typeof
  2. instanceof
  3. constructor
  4. Object.prototype.toString.call

如果你去面试,面试官问你怎么检测数据类型,你回答这四种是没有问题的,但是面试官问的不仅仅是这些,这仅仅是个开胃小菜而已,你真的了解这些数据类型检测的方法吗?下面我一一道来,看完这篇文章,了结数据类型检测的问题。

一、typeof

首先要了解typeof究竟能返回哪些值?贴图如下:

11.png

通过此图我们可以知道,typeof返回都是一个字符串,所有能返回的数据类型有:string、number、boolean、undefined、symbol、bigInt、object、function.

由此我们可以得出结论:typeof在检测基本数据类型的时候,除了null,检测的都很准确,在检测复杂数据类型的时候,除了检测函数外,其余的检测的都不准确。

那么为什么检测null返回的却是object呢?以下是我找到的资料

The “typeof null” bug is a remnant from the first version of JavaScript. In this version, values were stored in 32 bit units, which consisted of a small type tag (1–3 bits) and the actual data of the value. The type tags were stored in the lower bits of the units. There were five of them:

  • 000: object. The data is a reference to an object.

  • 1: int. The data is a 31 bit signed integer.

  • 010: double. The data is a reference to a double floating point number.

  • 100: string. The data is a reference to a string.

  • 110: boolean. The data is a boolean.

意思就是说:“typeof null”bug是JavaScript第一个版本的遗留问题。在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。 曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),但被拒绝了。该提案会导致 typeof null === 'null'。

typeof需要注意的点

  1. 除 Function 外的所有构造函数的类型都是 'object'。
var str = new String('String');

var num = new Number(100);

typeof str; // 返回 'object'

typeof num; // 返回 'object'

var func = new Function();

typeof func; // 返回 'function'
  1. 括号有无将决定表达式的类型。
let num = 99;

typeof num + ' JavaScript'; // 'number JavaScript'

typeof (num + ' JavaScript'); // 'string'
  1. 对正则表达式字面量的类型判断在某些浏览器中不符合标准。
typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1

typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1

二、instanceof

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

关于 instanceof 咱们先看下面一个例子:

let arr = [];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true

arr 是 Array的实例这是没问题的,但是arr 是 Object的实例 是不是有点困惑了?

别急,咱们再看下一个骚操作。

let arr = [];
arr.__proto__ = String.prototype
console.log(arr instanceof String);  // true

接下来咱们继续看:

console.log(1 instanceof Number);  // false
console.log(true instanceof Boolean);  // false
console.log('' instanceof String);  // false
console.log(null instanceof Object);  // false

由此我们可以得出结论:

  1. instanceof 底层机制:只要当前类出现在实例的原型链上,结果都是true
  2. 由于我们可以肆意的修改原型的指向,所以检测出来的结果是不准的
  3. 不能检测基本数据类型

三、constructor

constructor的准确解释为:

A constructor belongs to a particular class object that is instantiated. The constructor initializes this object and can provide access to its private information. The concept of a constructor can be applied to most object-oriented programming languages. Essentially, a constructor in JavaScript is usually declared at the instance of a class.

翻译为中文为:

构造函数属于被实例化的特定类对象 。构造函数初始化这个对象,并提供可以访问其私有信息的方法。构造函数的概念可以应用于大多数面向对象的编程语言。本质上,JavaScript 中的构造函数通常在类的实例中声明。

中文翻译还是有点不准,所以需要同学们平常顺带学学英文,毕竟英文的解释是最准确的。

talk is cheap,show me the code(话不多说,上代码,哈哈哈,这是日常程序员经常说的话。。。)

let arr = [];
console.log(arr.constructor === Array); // true
console.log(arr.constructor === RegExp); // false
console.log(arr.constructor === Object); // false

Number.prototype.constructor = 'AA';
let n = 1;
console.log(n.constructor === Number); // false 

以上可以得出结论:

  1. 支持基本类型的数据检测
  2. constructor可以随便改,所以也不准

四、Object.prototype.toString.call

这个应该是数据类型检测的标准写法,检测极其精确。他返回的是:[object XXX],其中XXX代表的就是要检测的数据类型。贴图如下:

222.png

toString()

每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中 type 是对象的类型。

他所能返回的值有:

"[object Number/String/Boolean/Null/Undefined/Symbol/Bigint/Error/Object/Array/RegExp/Date/Function]"

五、封装一个toType方法能检测所有数据类型

以下我就偷懒,贴下jQuery中的检测方法吧!话说jQuery的作者是很有技术水平的,写的代码也很经典。无聊的时候读读它的源码对编程的思维的培养还是很有帮助的。

//定义返回数据类型的映射表
var class2type = {};
// class2type.toString => 其实就是调用 Object.prototype.toString
var toString = class2type.toString;
//给 class2type 赋值 =>{[object String]:string,[object Number]:number,...}
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
	function( _i, name ) {
		class2type[ "[object " + name + "]" ] = name.toLowerCase();
	} );
// 真正去封装 toType 方法        
function toType( obj ) {
        //  由于 null == undefined  所以此处检测的是 null  和 undefined
	if ( obj == null ) {
		return obj + "";
        //  此处理解为 三元表达式 条件:(typeof obj === "object" || typeof obj === "function")
        // 调用 class2type[ toString.call( obj ) ] || "object"
        //  后面的 || "object"  的意思是 除了上面列举的常见类型,或许还有其他的复杂数据类型没有列出(例如:Math),统统返回 "object"
        
        // 否则 就是基本数据类型 直接用typeof 检测 返回值类型
	return typeof obj === "object" || typeof obj === "function" ?
		class2type[ toString.call( obj ) ] || "object" :
		typeof obj;
}

总结:小伙伴们,看完这篇文章,你是否对js中的数据类型检测有了新的认识?

ps:如有错误请评论指出,也请各位大牛能不吝赐教。谢谢!