JS中数据类型检测四种方式的优缺点以及其实现原理,最后赋js数据类型终极解决方案

644 阅读3分钟

一、typeof

1.语法: typeof value
2.返回值: 其返回是一个字符串类型的值,常见的有 "number""string""boolean""undefined""object""function""symbol""bigint"
3.优势:使用起来特别方便,检测基本类型和引用类型都可以检测
4.劣势:1.不能细分对象,比如检测 普通对象/数组对象/正则对象...等结果都是Object。就无法细分是普通 
         对象还是数组对象
       2.检测null类型的结果是Object,因为typeof 检测的机制是根据数据类型的值在计算机中存储编码
         特征来决定的,对象类型的值在计算中以000开始存储的,而null类型在计算机中全是以0存储的,
         所以被识别成对象了,而实际结果null不是对象,这是typeof的检测bug。
5.应用场景: 一般用于检测普通类型以及检测其是否是对象还是函数
            检测其是对象或者函数的代码片段如下:
            
    if (x !== null && /^(object|function)$/.test(typeof x)) {
	...
    }

二、instanceof

1.语法: value instanceof constructor
2.返回值:返回值是个boolean类型,只要实例的原型链有构造函数与其相等就返回true 否则返回false
3.优势:弥补typeof的不足可以细分对象类型,比如检测是否为数组,
4.劣势:1.检测的实例必须是引用类型
       2.基本数据类型的实例是无法基于它检测出来的
         1.字面量以及非通过new形式创建的实例都无法被检测
         2.通过new 构造函数的可以被检测
       3.检测的结果会不准如下代码
       
    var x = 10
    console.log(x instanceof Number) // false

    var y = Number(10)
    console.log(y instanceof Number) // false

    var z = new Number(10)
    console.log(z instanceof Number) // true
    
    
    var x = [];
    console.log(x instanceof Object); //true
    console.log(x instanceof Array)  //true
5.手写一个instanceof 的检测原理
    function _instanceof(value, Ctor) {
	let prototype = Object.getPrototypeOf(value);
	while (prototype) {
		if (prototype === Ctor.prototype) return true;
		prototype = Object.getPrototypeOf(prototype);
	}
	return false;
    }

三、constructor

1.语法:value.constructor === 类
2.原理:使用了类上原型的constructor属性指向类本身
3.优势: 使用起来简单并且可以检测基本数据类型
3.劣势: 类的原型容易被重构造成constructor丢失以及constructor属性容易被修改都会造成结果不准确

四、Object.prototype.toString.call()

1.背景: Object原型上的toString调用会返回传入实例的数据类型,而其他对象的toString方法都是把变量 
       转成字符串
2.语法:Object.prototype.toString.call()
3.返回值:  [Object class] 常见的值有[object Number]/[object String]/[object   
          Boolean]/[object Null]/[object Object]/[object Array]......
4.优势:可以检测任何数据类型
5.劣势:1.性能不如typeof
       2. 自定义类的数据类型检测不准 解决方案如下
       
    function Fn() {}
    if (typeof Symbol !== undefined) {
            Fn.prototype[Symbol.toStringTag] = 'Fn';
    }
    let fn = new Fn();
    console.log({}.toString.call(fn)); //[Object Fn]

五、基于jQuery源码封装的检测数据类型

(function () {
	var class2type = {};
	var toString = class2type.toString;
	var mapType = [
		'Boolean',
		'Number',
		'String',
		'Function',
		'Array',
		'Date',
		'RegExp',
		'Object',
		'Error',
		'Symbol',
		'BigInt',
	];
	mapType.forEach(function (name) {
		class2type['[object ' + name + ']'] = name.toLocaleLowerCase();
	});
	var toType = function toType(obj) {
		if (obj == null) {
			return obj + '';
		}
		return typeof obj === 'object' || typeof obj === 'function'
			? class2type[toString.call(obj)] || 'object'
			: typeof obj;
	};
	/* 暴露API「支持浏览器直接导入 & webpack CommonJS模块导入」 */
	if (typeof window !== 'undefined') {
		window.toType = toType;
	}
	if (typeof module === 'object' && typeof module.exports === 'object') {
		module.toType = toType;
	}
  })();