如何准确判断 JS 中的数据类型? instanceof 和 typeof 有什么区别?
- 前端
- 3天前
- 17热度
- 0评论
在JavaScript开发中,35%的bug源于数据类型判断错误。当使用typeof检测数组时得到"object",用instanceof检查跨窗口对象时返回false,这些陷阱让开发者头疼不已。本文将深入解析typeof与instanceof的核心差异,揭密Object.prototype.toString.call的终极解决方案,并手把手教你实现类型判断的完美闭环。
一、typeof运算符:基础但局限
基本语法:
```javascript
console.log(typeof 42); // "number"
console.log(typeof 'text'); // "string"
```
1.1 可识别的7种基本类型
- undefined: typeof undefined ➔ "undefined"
- boolean: typeof true ➔ "boolean"
- number: typeof NaN ➔ "number"(特殊值仍返回number)
- string: typeof `template` ➔ "string"
- symbol: typeof Symbol() ➔ "symbol"
- bigint: typeof 10n ➔ "bigint"
- function: typeof function(){} ➔ "function"
1.2 令人困惑的null检测
```javascript
typeof null; // "object" (历史遗留问题)
```
这个结果导致无法用typeof区分null与普通对象,需配合严格相等判断:
```javascript
const isNull = value => value === null;
```
二、instanceof运算符:原型链检测器
运行原理:
```javascript
obj instanceof Constructor
// 等价于
Constructor.prototype.isPrototypeOf(obj)
```
2.1 典型应用场景
```javascript
[] instanceof Array; // true
new Date() instanceof Date; // true
```
2.2 三大核心缺陷
- 跨窗口检测失效:iframe中的数组不是主窗口Array的实例
- 原始类型误判:1 instanceof Number ➔ false
- 原型污染风险:修改prototype会导致判断错误
三、终极解决方案:Object.prototype.toString.call
标准实现方案:
```javascript
function getType(obj) {
return Object.prototype.toString.call(obj)
.replace(/\[object\s(.+)\]/, "$1").toLowerCase();
}
// 测试用例
getType(null); // "null"
getType([]); // "array"
getType(/regex/); // "regexp"
```
3.1 支持识别的完整类型
返回值 | 对应类型 |
---|---|
[object Null] | null |
[object Undefined] | undefined |
[object Boolean] | 布尔值 |
[object Number] | 数值 |
[object String] | 字符串 |
[object Array] | 数组 |
[object Function] | 函数 |
[object Date] | 日期对象 |
四、手写实现instanceof
通过原型链递归实现:
```javascript
function myInstanceof(obj, constructor) {
let proto = Object.getPrototypeOf(obj);
while(proto) {
if(proto === constructor.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
}
```
五、TypeScript中的进阶实践
5.1 类型守卫(Type Guards)
```typescript
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processValue(value: unknown) {
if (isString(value)) {
console.log(value.toUpperCase()); // 安全调用字符串方法
}
}
```
5.2 unknown与any的抉择
推荐方案:
```typescript
let value: unknown = "Hello";
// 安全写法
let length = (value as string).length;
// 危险写法(失去类型检查)
let length = (value as any).length;
```
总结:类型判断四层防御体系
- 第一层:typeof检测基本类型
- 第二层:=== null判断空值
- 第三层:Object.prototype.toString.call识别复杂类型
- 第四层:instanceof验证自定义对象
通过组合使用这些方法,可构建覆盖所有场景的类型判断方案。在TypeScript项目中,配合类型守卫和unknown类型,更能将类型错误扼杀在编译阶段。