JavaScript类型检查的完整指南
JavaScript的动态类型是其最强大的特性之一。然而,对于经验不足的开发人员来说,它经常是问题和困惑的根源。因此,类型检查是任何JavaScript开发人员的关键技能。
值类型
在我们深入探讨JavaScript中不同的类型检查方式之前,让我们先看一下我们在JavaScript中可能遇到的不同值类型。
| 类型 | 描述 | 示例 |
|---|---|---|
undefined |
尚未分配值的变量的值。 | let x; |
null |
任何对象值的有意缺失。 | const x = null; |
boolean |
逻辑值,要么是true,要么是false。 |
const x = true; |
number |
数值。 | const x = 50; |
bigint |
具有任意精度的整数。 | const x = 9007199254740991n; |
string |
字符串序列。 | const x = 'Hello!'; |
symbol |
可用作对象属性键的唯一值。 | const x = Symbol(); |
object |
属性的集合。 | const x = { a: 1, b: 2 }; |
function |
可调用的对象。 | const x = () => {}; |
除了最后两种类型,所有其他类型都被视为原始类型。原始类型是不可变的,这意味着它们的值不能被改变。而对象和函数则是可变的,这意味着它们的值可以被改变。
检查原始类型
与对象和函数相比,原始类型通常更容易进行类型检查。这是因为原始类型是不可变的,这意味着它们的值不能被改变。因此,我们可以简单地将一个值的类型与我们要检查的类型进行比较。这就是typeof运算符派上用场的地方。
值是undefined
检查undefined值就像将该值与undefined进行比较一样简单。这与使用typeof运算符得到的结果完全相同。
const isUndefined = val => val === undefined;
isUndefined(undefined); // true
值为null
检查null值只能通过将值与null本身进行比较。这是因为typeof null返回'object',这不是我们想要的。
const isNull = val => val === null;
isNull(null); // true
值为nil
在其他一些语言中,使用nil值来表示缺少值。然而,在JavaScript中,null和undefined用于此目的。因此,检查nil值与检查null或undefined值是相同的。
const isNil = val => val === undefined || val === null;
isNil(null); // true
isNil(undefined); // true
isNil(''); // false
值是布尔类型
布尔值只能是true或false。检查这两个值相对低效,所以通常更倾向于使用typeof。
const isBoolean = val => typeof val === 'boolean';
isBoolean(true); // true
isBoolean(false); // true
isBoolean('true'); // false
isBoolean(null); // false
值是数字类型
数字也可以使用typeof进行类型检查。然而,这也会对NaN返回true,NaN是一个特殊的数值,表示无法产生正常结果的操作的结果。
你可以阅读更多关于NaN以及为什么它可能很棘手,但通常建议使用Number.isNaN()来额外检查NaN。
const isNumber = val => typeof val === 'number' && !Number.isNaN(val);
isNumber(1); // true
isNumber('1'); // false
isNumber(NaN); // false
值是大整数类型
BigInt是JavaScript中相对较新的添加。它们是具有任意精度的整数。使用typeof对BigInt进行类型检查。
const isBigInt = val => typeof val === 'bigint';
isBigInt(1n); // true
isBigInt(1); // false
值是字符串
像大多数其他原始类型一样,字符串原始类型可以使用typeof进行类型检查。
const isString = val => typeof val === 'string';
isString('Hello!'); // true
isString(1); // false
值是符号
符号是可以用作对象属性键的唯一值。它们也可以使用typeof进行类型检查。
const isSymbol = val => typeof val === 'symbol';
isSymbol(Symbol('x')); // true
isSymbol('x'); // false
值是原始类型
检查一个值是否是任何原始类型有点棘手。我们不能简单地使用typeof来判断,因为它对null不起作用。
相反,我们可以从值创建一个对象,然后将其与值本身进行比较。如果值是原始类型,对象将不等于值。
const isPrimitive = val => Object(val) !== val;
isPrimitive(null); // true
isPrimitive(undefined); // true
isPrimitive(50); // true
isPrimitive('Hello!'); // true
isPrimitive(false); // true
isPrimitive(Symbol()); // true
isPrimitive([]); // false
isPrimitive({}); // false
检查非原始类型
对象和函数的行为与原始类型略有不同。虽然typeof可以帮助我们一部分,但我们可能还想使用其他方法,特别是对于对象。
值是对象
如前所述,typeof对于null返回'object'。这不是我们想要的,当我们检查对象时。相反,我们可以使用Object构造函数为给定的值创建一个对象包装器。如果值是null或undefined,返回的值将是一个空对象。否则,返回的对象将与输入对象相同。
const isObject = obj => obj === Object(obj);
isObject([1, 2, 3, 4]); // true
isObject([]); // true
isObject(['Hello!']); // true
isObject({ a: 1 }); // true
isObject({}); // true
isObject(true); // false
isObject(null); // false
isObject(undefined); // false
[!TIP]
在JavaScript中,数组也被视为对象。建议使用
Array.isArray()来检查一个值是否为数组。
值是函数
幸运的是,函数并不像对象那样棘手。我们可以简单地使用typeof来检查一个值是否为函数。
const isFunction = val => typeof val === 'function';
isFunction(x => x); // true
isFunction('x'); // false
[!NOTE]
在JavaScript中,类也被视为函数。您可能希望使用
instanceof运算符来检查一个值是否为类的实例。
值是异步函数
使用async关键字声明的函数被视为异步函数。对于异步函数的类型检查,需要使用Object.prototype.toString()和Function.prototype.call()来检查结果是否为'[object AsyncFunction]'。
const isAsyncFunction = val =>
Object.prototype.toString.call(val) === '[object AsyncFunction]';
isAsyncFunction(function() {}); // false
isAsyncFunction(async function() {}); // true
值是生成器函数
生成器函数可以像异步函数一样进行类型检查。它们的预期值是'[object GeneratorFunction]'。
const isGeneratorFunction = val =>
Object.prototype.toString.call(val) === '[object GeneratorFunction]';
isGeneratorFunction(function() {}); // false
isGeneratorFunction(function*() {}); // true
值的类型
如果其他方法都失败了,或者你正在使用类和其他自定义类型,你可能想要获取一个值的类型的字符串表示。可以使用Object.prototype.constructor和Function.prototype.name来实现。
const getType = v =>
v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name;
getType(undefined); // 'undefined'
getType(null); // 'null'
getType(true); // 'Boolean'
getType(1); // 'Number'
getType(1n); // 'BigInt'
getType('Hello!'); // 'String'
getType(Symbol()); // 'Symbol'
getType([]); // 'Array'
getType({}); // 'Object'
getType(() => {}); // 'Function'
getType(new Set([1, 2, 3])); // 'Set'
检查值的类型
将前面的代码片段反过来,我们也可以检查一个值是否是特定类型。与之前一样,对于undefined和null需要特别注意,因为它们没有constructor属性。
const isOfType = (type, val) =>
([undefined, null].includes(val) && val === type) ||
val.constructor.name === type;
isOfType(undefined, undefined); // true
isOfType(null, null); // true
isOfType('Boolean', true); // true
isOfType('Number', 1); // true
isOfType('BigInt', 1n); // true
isOfType('String', 'Hello!'); // true
isOfType('Symbol', Symbol()); // true
isOfType('Array', []); // true
isOfType('Object', {}); // true
isOfType('Function', () => {}); // true
isOfType('Set', new Set([1, 2, 3])); // true