JS数据类型检测

344 阅读4分钟

1、typeof

在JS中,能用typeof能检测出来的数据类型,最好使用typeof来检测。

typeof是基本数据类型检测利器(但是不包括null);也可以检测是否为一个函数,但是在检测对象时,并不能为每个对象明确指出是属于哪一类,因为返回值都是object(无法检测出明确的引用数据类型)。

typeof返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function等7种数据类型,但不能判断null、array

typeof 1;  // 'number' 有效
typeof true;  // 'boolean' 有效
typeof '';  // 'string' 有效
typeof undefined;  // 'undefined' 有效
typeof function (){}; // 'function' 有效
typeof new Function(); // 'function' 有效
typeof Symbol(); // 'symbol' 有效

typeof null; // 'object' 无效
typeof []; // 'object' 无效
typeof new Date(); // 'object' 无效
typeof new RegExp(); // 'object' 无效

2、instanceof

instanceof是一个操作符,返回值是一个布尔值。
instanceof是检测引用数据类型,而不能检测基本数据类型。

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测nullundefined

let arr = [];
arr instanceof Array;  // true

[] instanceof Array;  // true
{} instanceof Object;  // true
new Date() instanceof Date;  // true
new RegExp() instanceof RegExp; // true

null instanceof Null; // 报错
undefined instanceof undefined; // 报错

但是只要是在原型链上出现过构造函数都会返回true,所以这个检测结果不很准确

arr instanceof Object;  // true

3、constructor

constructor这个属性存在于构造函数的原型上,指向构造函数,对象可以通过__proto__在其所属类的原型上找到这个属性

let arr = [];
arr.constructor === Array; // true
arr.constructor === Object; // false
// 因为arr通过原型链查找到的constructor指向了Array,所以跟Object判断就是错误滴

constructor是比较准确判断对象的所属类型的,但是如果有继承的话
function Fn() { }
Fn.prototype = new Array();

let fn = new Fn();
console.log(fn.constructor === Array);
// fn是一个对象,但是它的构造函数指向Array竟然是true 因此这个方法要慎用

4、Object.prototype.toString.call()

Object基本类定义的这个toString()方法,是用来检测数据类型的;跟字符串、数字、布尔等原型上定义的toString()方法基本用法都是转换字符串的。

这对数据检测应该是最准确的

console.log(Object.prototype.toString.call(1));  // [object Number]
console.log(Object.prototype.toString.call(''));  // [object String]
console.log(Object.prototype.toString.call(true));  // [object Boolean]
console.log(Object.prototype.toString.call(null));  // [object Null]
console.log(Object.prototype.toString.call(undefined));  // [object Undefined]
console.log(Object.prototype.toString.call([]));  // [object Array]
console.log(Object.prototype.toString.call({}));  // [object Object]
console.log(Object.prototype.toString.call(/^$/));  // [object RegExp]
console.log(Object.prototype.toString.call((function () {})));  // [object Function]
console.log(Object.prototype.toString.call(new Function()));  // [object Function]
console.log(Object.prototype.toString.call(new Error()));  // [object Error]
console.log(Object.prototype.toString.call(new RegExp()));  // [object RegExp]
console.log(Object.prototype.toString.call(new Date()));  // [object Date]
console.log(Object.prototype.toString.call(JSON));  // [object JSON]
console.log(Object.prototype.toString.call(Math));  // [object Math]
console.log(Object.prototype.toString.call(window));  // [object global]
console.log(Object.prototype.toString.call(Symbol()));  // [object Symbol]

因为typeof检测引用类型都会返回"object",所以不是一种检测引用类型的好方式.

instanceof检测引用类型(内置的引用类型:Object、Array、Date、Error等):
value instanceof constructor(语法)

instanceof除了可以检测内置的引用类型,同样可以检测自定义的类型.
typeof检测基本数据类型,除了null,其他数据都可以检测出正确的类型;
typeof检测对象,除了函数都会显示object;

获取一个变量的正确类型的最通用方法:Object.prototype.toString.call(xx)

// 通过typeof(x)可以返回一个变量x的数据类型"number"、"string"、"boolean"、"undefined"、"object"、"function"
console.log(typeof null == "object");
console.log(typeof {} == "object");
console.log(typeof [] == "object");
console.log(typeof undefined == "undefined");

console.log(Object.prototype.toString.call([]) == "[object Array]"); // 检测类型
console.log(Object.prototype.toString.call({}));  // [object Object]
console.log(Object.prototype.toString.call([]));  // [object Array]
console.log(Object.prototype.toString.call(null));  // [object Null]

console.log(isNaN(NaN));    // true
console.log(isNaN(23));       // false
console.log(isNaN('ds'));      // true
console.log(isNaN('32131sdasd')); // true
console.log(NaN === NaN);            // false
console.log(NaN === undefined);  // false
console.log(undefined === undefined); // true ->google
console.log(typeof NaN);                        // number
console.log(Object.prototype.toString.call(NaN));  // [object Number]

总结:

JavaScript内置的类型检测机制并非完全可靠。事实上,发生错误否定及错误肯定的情况也不在少数。比如说typeof操作符吧,由于它有一些无法预知的行为,经常会导致检测数据类型时得到不靠谱的结果。instanceof操作符在存在多个全局作用域(像一个页面包含多个frame)的情况下,也是问题多多。

解决方法: 任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属性,这个属性中就指定了上述字符串中的构造函数名。

console.log(Object.prototype.toString.call(value)); // "[object Array]"

由于原生数组的构造函数名与全局作用域无关,因此使用toString()就能保证返回一致的值。

// 检测数组
function isArray(value) {
    return Object.prototype.toString.call(value) == "[object Array]";
}
// 检测方法
function isFunction(value) {
    return Object.prototype.toString.call(value) == "[object Function]";
}
// 检测正则表达式
function isRegExp(value) {
    return Object.prototype.toString.call(value) == "[object RegExp]";
}

类型判断函数封装

/**
 * 类型判断函数
 * @param value
 * @returns {"string"|"undefined"|"object"|"boolean"|"number"|"string"|"function"|"symbol"|"bigint"}
 */
function getType (value) {
  // 判断数据是 null 的情况
  if (value === null) return value + ''

  // 判断数据是引用类型的情况
  if (typeof value === 'object') {
    // '[object Array]' / '[object Function]' / '[object String]' / '[object Object]'
    let type = Object.prototype.toString.call(value).split(' ')[1].split('')

    type.pop() // ] -> 去掉

    return type.join('').toLowerCase()
  } else {
    // 判断数据是基本数据类型的情况和函数的情况
    return typeof value // number、booleanstring、symbol、object、undefined、function
  }
}