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 属性,但它不能检测null 和 undefined
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、boolean、string、symbol、object、undefined、function
}
}