JavaScript 是一门弱类型的语言,在定义变量的时候,不能事先声明变量的类型。变量的类型只有真正运行代码的时候才知道。所以如果不做类型判断的话,就可能出现类似下面的错误:
function sum(a, b) {
return a + b;
}
sum(1, 1) // 2
sum('1', 1) // '11'
sum() // NaN
虽然在运行 sum('1',1) 和 sum() 的时候,函数并没有报错(这是因为 JavaScript 的自动类型转换机制),但是传给函数的参数显然不符合我们的预期。我们期望 a 和 b 是数字,而不是其他数据类型。
为了解决这个问题,我们需要对变量的类型进行判断。JavaScript 提供了两个表达式 typeof 和 instanceof 来帮助我们判断数据类型。在具体了解这两个表达式之前,我们需要先了解下 JavaScript 有哪些数据类型。
JavaScript 的数据类型
JavaScript 的数据类型分为基本数据类型和引用数据类型。
基本数据类型
基本数据类型一共有 7 种,分别是:
- String
- Number
- Boolean
- undefined
- null
- Symbol
- BigInt
基本数据类型的特点是值不可修改,例如:
// s 属于 String 类型, 它的值是不可修改的
let s = 'hello,world'
s[0] = 'x'
console.log(s) // hello,world
引用数据类型
引用数据类型只有 Object,当然 JavaScript 还提供了很多继承自 Object 的内置数据类型,比如
- Function
- Array
- Date
- RegExp
- ...
typeof
typeof 是一个表达式,用法是:typeof x ,返回值表示 x 的数据类型。
let a = 1;
if (typeof a === 'number') {
// do something...
}
typeof 1 // 'number'
typeof 1n // 'bigint'
typeof true // 'boolean'
typeof 'hello' // 'string'
typeof null // 'object'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof {} // 'object'
typeof [] // 'object'
typeof new Date() // 'object'
typeof (() => {}) // 'function'
对于基本数据类型, typeof 能够进行很好的区分。唯一的例外是 null,返回的是 'object'。
对于引用数据类型, typoef 只能区分出 Function
所以 typeof 能够判断数据类型是否属于 Number, String, Boolean, undefined, Symbol 等基本数据类型或者 Function。对于其他的数据类型, typeof 无法做出区分。
现在,我们可以对 sum 函数进行改进,在传入非数字参数的时候,抛出错误:
/* JavaScript */
function sum(a, b) {
if (typeof a !== 'number') {
throw new Error('a 不是数字!')
}
if (typeof b !== 'number') {
throw new Error('b 不是数字!')
}
return a + b;
}
sum(1, 1) // 2
sum('1', 1) // error
sum(). // error
现在,当传入函数的参数不是数字时, sum 函数便会报错。
但是,考虑下面这种情况:
function sum(a, b) {
if (typeof a !== 'number') {
throw new Error('a 不是数字!')
}
if (typeof b !== 'number') {
throw new Error('b 不是数字!')
}
return a + b;
}
let a = new Number(1);
let b = 1;
sum(a, b) // error
a 被包裹在 Number 对象里, typeof 判断失效了。
instanceof
instanceof 用来判断一个对象是否属于某个类的实例,用法: a instanceof b,且 b 必须是一个构造函数,如果 a 属于 b 的实例,返回 true,否则返回 false。
[] instanceof Array; // true
({}) instanceof Object; // true
new Date() instanceof Date; // true
[] instanceof Date; // false
new Number() instanceof Number; // true
[] instanceof Number; // false
使用 instanceof ,我们就可以区分出 Date , Array 等引用类型了。
sum 函数也可以进一步完善:
function sum(a, b) {
if (typeof a !== 'number' && !(a instanceof Number)) {
throw new Error('a 不是数字!')
}
if (typeof b !== 'number' && !(b instanceof Number)) {
throw new Error('b 不是数字!')
}
return a + b;
}
let a = new Number(1);
let b = 1;
sum(a, b) // 2
BigInt
BigInt 是 ES2020 引入的基本数据类型,用来支持任意精度的数字运算,使用的时候,在常规的数字后面加上字母 n 或者使用 BigInt() 函数来生成。
function sum(a, b) {
if (typeof a !== 'number' && !(a instanceof Number)) {
throw new Error('a 不是数字!')
}
if (typeof b !== 'number' && !(b instanceof Number)) {
throw new Error('b 不是数字!')
}
return a + b;
}
let a = 1n;
let b = 1n;
sum(a, b) // error
在上面的例子中, sum 报错了,是因为 typeof 1n 返回的是 bigint, 并且, BigInt 属于基本数据类型, 不属于 Number 对象的实例。要兼容 BigInt 的话,我们还需要做额外的判断:
function sum(a, b) {
if (typeof a !== 'bigint' && typeof a !== 'number' && !(a instanceof Number)) {
throw new Error('a 不是数字!')
}
if (typeof b !== 'bigint' && typeof b !== 'number' && !(b instanceof Number)) {
throw new Error('b 不是数字!')
}
return a + b;
}
let a = 1n;
let b = 1n;
sum(a, b) // 2n