[JS] 数据类型与特殊值的判断方法

cnblogs 2024-07-01 08:11:00 阅读 92

JavaScript 是一种弱类型语言,开发者经常需要判断变量的数据类型。本文介绍了 typeof、instanceof、Object.prototype.toString.call 等常用的判断方法,并讨论了 typeof null 返回 object 的历史原因。此外,还涵盖了如何判断箭头函数、async 函数和类。

由于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类型。

由于这个错误影响范围很大,后期并没有被修复。

👉harmony:typeof_null [ES Wiki] (archive.org)

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作为原生的方法,底层实现会被引擎优化,通常比起后者的字符串比较操作性能会更好。

判断箭头函数

箭头函数的特点:

  1. toString方法返回函数体会包含=>;(这个特点作为判断标准不严谨,因为普通函数的函数体可能包含带有=>字符的语句)
  2. 箭头函数没有prototype属性,而普通函数有;
  3. 箭头函数不能被当作构造函数,因此使用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函数的特点:

  1. toString方法返回的字符串带有async(开头位置);
  2. Object.prototype.toString.call会返回[object AsyncFunction]
  3. 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

常见方法:

  1. 使用 typeofFunction.prototype.toString:通过 typeof 检查是否是函数,然后通过 toString 检查字符串表示形式中是否包含 class 关键字;
  2. 检查原型链:类的原型链上通常会有 constructor 属性,并且这个 constructor 属性指向类自身;
  3. 使用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 /^class\s/.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 /^class\s/.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]';

}


上一篇: 将File对象转换为URL

下一篇: Vue

本文标签

随笔    JS    前端知识库    javascript   


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。