- A+
所属分类:Web前端
由于JS是弱类型语言,判断一个变量的数据类型是一个很常见的需求。
下面介绍一些常用的判断方法:
typeof操作符
typeof
可以用来判断除了``null的基本数据类型和
function,其它引用数据类型都会返回
object`。
console.log(typeof "Hello"); // "string" console.log(typeof 42); // "number" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof function(){}); // "function"
console.log(typeof null); // "object" (这是一个历史遗留的bug) console.log(typeof []); // "object"
为什么typeof null会返回object ?
在JS的最初版本中,使用32位二进制表示栈中的变量,二进制的前三位为类型标识tag,当前三位都是0时,表示object类型。但是null被设计为32位二进制都是0,因此会被错误地识别为object类型。
由于这个错误影响范围很大,后期并没有被修复。
instanceof操作符
语法:变量 instanceof 函数
;
返回值:布尔值,变量是否是指定的构造函数的实例,即变量的原型链上是否存在指定的构造函数。
特点:只用来判断引用数据类型。
console.log([] instanceof Array); // true console.log({} instanceof Object); // true console.log(function(){} instanceof Function); // true console.log(new Date() instanceof Date); // true
对于基础数据类型:
1 instanceof Number ==> false let a = new Number(1); a instanceof Number ==> true
注意:instanceof
在跨iframe或者不同的JavaScript执行环境时可能会失效,因为每个执行环境都有独立的构造函数。
Object.prototype.toString.call
这是最通用和可靠的方法。通过Object.prototype.toString.call
方法,可以精确地判断变量的类型,不受执行环境的影响。
console.log(Object.prototype.toString.call("Hello")); // "[object String]" console.log(Object.prototype.toString.call(42)); // "[object Number]" console.log(Object.prototype.toString.call(true)); // "[object Boolean]" console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" console.log(Object.prototype.toString.call(null)); // "[object Null]" console.log(Object.prototype.toString.call([])); // "[object Array]" console.log(Object.prototype.toString.call({})); // "[object Object]" console.log(Object.prototype.toString.call(function(){})); // "[object Function]" console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
isArray
Array.isArray在ES5就存在了,与上述的Object.prototype.toString.call
方法相比:
- Array.isArray的兼容性没有后者好,但是考虑到IE目前已经无了,基本可以放心使用;
- Array.isArray作为原生的方法,底层实现会被引擎优化,通常比起后者的字符串比较操作性能会更好。
判断箭头函数
箭头函数的特点:
toString
方法返回函数体会包含=>
;(这个特点作为判断标准不严谨,因为普通函数的函数体可能包含带有=>
字符的语句)- 箭头函数没有
prototype
属性,而普通函数有; - 箭头函数不能被当作构造函数,因此使用
new
关键字实例化会抛出异常。
综合判断方法:
function isArrowFunction(func) { if (typeof func !== 'function') { return false; } try { new func(); return false; } catch (e) { return !func.hasOwnProperty('prototype') && func.toString().includes('=>'); } }
判断async函数
async函数的特点:
toString
方法返回的字符串带有async
(开头位置);Object.prototype.toString.call
会返回[object AsyncFunction]
;- 是
AsyncFunction
构造函数的实例。(由于在大多数环境中,AsyncFunction
无法直接访问,可以通过构建一个新的async
函数来获得这个构造函数)。
综合判断方法:
function isAsyncFunction(func) { if (typeof func !== 'function') { return false; } const AsyncFunction = (async function() {}).constructor; return func instanceof AsyncFunction || Object.prototype.toString.call(func) === '[object AsyncFunction]' || func.toString().trim().startsWith('async'); }
判断class
常见方法:
- 使用
typeof
和Function.prototype.toString
:通过typeof
检查是否是函数,然后通过toString
检查字符串表示形式中是否包含class
关键字; - 检查原型链:类的原型链上通常会有
constructor
属性,并且这个constructor
属性指向类自身; - 使用
new
检查:类不能在没有new
关键字的情况下调用,而函数可以。
综合方法:
function isClass(func) { if (typeof func !== 'function') { return false; } try { func(); return false; } catch (e) { if (e.message.includes('Class constructor') || e.message.includes('class constructors')) { return true; } return /^classs/.test(Function.prototype.toString.call(func)); } }
示例文件
// is.js /** * 判断是否为字符串 * @param value - 需要判断的值 * @returns boolean */ function isString(value) { return Object.prototype.toString.call(value) === '[object String]'; } /** * 判断是否为数字 * @param value - 需要判断的值 * @returns boolean */ function isNumber(value) { return Object.prototype.toString.call(value) === '[object Number]'; } /** * 判断是否为布尔值 * @param value - 需要判断的值 * @returns boolean */ function isBoolean(value) { return Object.prototype.toString.call(value) === '[object Boolean]'; } /** * 判断是否为 undefined * @param value - 需要判断的值 * @returns boolean */ function isUndefined(value) { return Object.prototype.toString.call(value) === '[object Undefined]'; } /** * 判断是否为 null * @param value - 需要判断的值 * @returns boolean */ function isNull(value) { return Object.prototype.toString.call(value) === '[object Null]'; } /** * 判断是否为数组 * @param value - 需要判断的值 * @returns boolean */ function isArray(value) { return Array.isArray(value); // return Object.prototype.toString.call(value) === '[object Array]'; } /** * 判断是否为对象 * @param value - 需要判断的值 * @returns boolean */ function isObject(value) { return Object.prototype.toString.call(value) === '[object Object]'; } /** * 判断是否为函数 * @param value - 需要判断的值 * @returns boolean */ function isFunction(value) { return Object.prototype.toString.call(value) === '[object Function]'; } /** * 判断是否为日期 * @param value - 需要判断的值 * @returns boolean */ function isDate(value) { return Object.prototype.toString.call(value) === '[object Date]'; } /** * 判断是否为正则表达式 * @param value - 需要判断的值 * @returns boolean */ function isRegExp(value) { return Object.prototype.toString.call(value) === '[object RegExp]'; } /** * 判断是否为错误对象 * @param value - 需要判断的值 * @returns boolean */ function isError(value) { return Object.prototype.toString.call(value) === '[object Error]'; } /** * 判断是否为 Symbol * @param value - 需要判断的值 * @returns boolean */ function isSymbol(value) { return Object.prototype.toString.call(value) === '[object Symbol]'; } /** * 判断是否为 Promise * @param value - 需要判断的值 * @returns boolean */ function isPromise(value) { return Object.prototype.toString.call(value) === '[object Promise]'; } /** * 判断是否为 Set * @param value - 需要判断的值 * @returns boolean */ function isSet(value) { return Object.prototype.toString.call(value) === '[object Set]'; } /** * 判断是否为 Map * @param value - 需要判断的值 * @returns boolean */ function isMap(value) { return Object.prototype.toString.call(value) === '[object Map]'; } /** * 判断是否为 箭头函数 * @param value - 需要判断的值 * @returns boolean */ function isArrowFunction(func) { if (typeof func !== 'function') { return false; } try { new func(); return false; } catch (e) { return !func.hasOwnProperty('prototype') && func.toString().includes('=>'); } } /** * 判断是否为 async函数 * @param value - 需要判断的值 * @returns boolean */ function isAsyncFunction(func) { if (typeof func !== 'function') { return false; } const AsyncFunction = (async function() {}).constructor; return func instanceof AsyncFunction || Object.prototype.toString.call(func) === '[object AsyncFunction]' || func.toString().trim().startsWith('async'); } /** * 判断是否为 class * @param value - 需要判断的值 * @returns boolean */ function isClass(func) { if (typeof func !== 'function') { return false; } try { func(); return false; } catch (e) { if (e.message.includes('Class constructor') || e.message.includes('class constructors')) { return true; } return /^classs/.test(Function.prototype.toString.call(func)); } } /** * 判断是否为 空对象 * @param value - 需要判断的值 * @returns boolean */ function isEmptyObject(value) { return isObject(value) && Object.keys(value).length === 0; } // 导出函数 module.exports = { // type isString, isNumber, isBoolean, isUndefined, isNull, isArray, isObject, isFunction, isDate, isRegExp, isError, isSymbol, isPromise, isSet, isMap, isArrowFunction, isAsyncFunction, isClass, // value isEmptyObject, };
值比较
除了类型比较,JS里有一些值也是经常需要判断的。
NaN、Infinitity、Integer、safeInteger
这些和数值相关的判断都在Number的静态方法里了。
Number.isNaN(value); // 是否NaN Number.isFinite(value); // 是否有限数值 function isInfinitiy(value){ // 是否是无穷大 return !Number.isFinite(value); // 另一种写法 return value === Infinity || value === -Infinity; } Number.isInteger(value); // 判断整数 Number.isSafeInteger(value); // 判断安全整数
判断空对象
空对象指的是不包含任何可枚举属性的对象。
function isEmptyObject(value) { return isObject(value) && Object.keys(value).length === 0; } function isObject(value) { return Object.prototype.toString.call(value) === '[object Object]'; }