# 类型判断:typeof/instanceof的替代方案
在JavaScript开发中,准确判断变量类型是日常工作中不可或缺的一部分。虽然`typeof`和`instanceof`是最常用的类型判断方法,但它们各自存在局限性,我们需要更可靠的替代方案。
## 传统方法的局限性
### typeof的缺陷
```javascript
typeof null // "object" (历史遗留问题)
typeof [] // "object"
typeof new Date() // "object"
typeof /regex/ // "object" (ES5及之前)
```
`typeof`只能准确判断基本类型,对于对象类型几乎都会返回"object",无法区分数组、日期、正则表达式等。
### instanceof的问题
```javascript
[] instanceof Array // true
[] instanceof Object // 也返回true
const iframe = document.createElement('iframe')
document.body.appendChild(iframe)
const iframeArray = iframe.contentWindow.Array
const arr = new iframeArray()
arr instanceof Array // false (不同全局环境)
```
`instanceof`检查原型链,可能产生误判,且在跨框架/iframe场景下会失效。
## 现代替代方案
### Object.prototype.toString.call()
```javascript
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(/regex/) // "[object RegExp]"
```
这是最可靠的类型判断方法,可以精确识别所有内置类型。
### 实用封装函数
```javascript
function typeOf(obj) {
return Object.prototype.toString.call(obj)
.slice(8, -1)
.toLowerCase()
}
typeOf([]) // "array"
typeOf(new Map()) // "map"
typeOf(Symbol()) // "symbol"
typeOf(async () => {}) // "asyncfunction"
```
### 自定义类型判断
```javascript
function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
&& Object.getPrototypeOf(obj) === Object.prototype
}
function isPromise(obj) {
return (obj instanceof Promise) || (
obj !== null &&
typeof obj === 'object' &&
typeof obj.then === 'function' &&
typeof obj.catch === 'function'
)
}
```
### ES6+新增方法
```javascript
Array.isArray([]) // true
Number.isNaN(NaN) // true (比全局isNaN更可靠)
Number.isInteger(3) // true
Number.isFinite(1) // true
```
## 实际应用场景
1. **API参数验证**:
```javascript
function fetchData(options) {
if (!isPlainObject(options)) {
throw new TypeError('Options must be a plain object')
}
// ...
}
```
2. **函数式编程中的类型守卫**:
```javascript
function processInput(input) {
if (typeOf(input) === 'array') {
return input.map(processItem)
}
if (typeOf(input) === 'object') {
return Object.values(input).map(processItem)
}
return processItem(input)
}
```
3. **跨环境通信的类型检查**:
```javascript
function safeClone(obj) {
const type = typeOf(obj)
if (type === 'date') return new Date(obj)
if (type === 'regexp') return new RegExp(obj)
// ...其他特殊类型处理
return JSON.parse(JSON.stringify(obj))
}
```
## 性能考量
虽然`Object.prototype.toString.call()`是最可靠的方案,但在性能敏感场景下,可以结合多种方法:
```javascript
function fastTypeOf(obj) {
if (obj === null) return 'null'
if (typeof obj !== 'object') return typeof obj
if (Array.isArray(obj)) return 'array'
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}
```
## 总结
1. 基本类型:使用`typeof`
2. `null`检查:严格比较`obj === null`
3. 数组:优先使用`Array.isArray()`
4. 其他对象类型:使用`Object.prototype.toString.call()`
5. 自定义类型:结合原型检查和特征检查
根据具体场景选择合适的方法,在可靠性和性能之间取得平衡。在现代JavaScript开发中,类型判断仍然是保证代码健壮性的重要手段。