【前端面试小册】JS-第27节:实现类型判断
一、核心概念
1.1 为什么需要类型判断
JavaScript 是动态类型语言,变量的类型在运行时才能确定。准确判断变量类型对于:
- 参数验证
- 错误处理
- 类型转换
- 代码健壮性
都非常重要。
1.2 类型判断方法对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
typeof |
简单快速 | 无法区分对象类型 | 基本类型判断 |
instanceof |
判断实例 | 无法判断基本类型 | 对象实例判断 |
Object.prototype.toString |
最准确 | 语法稍复杂 | 精确类型判断 |
二、实现类型判断
2.1 基础实现
// 获取类型
const getType = (type) => {
return Object.prototype.toString.call(type).match(/\s+(\w+)/)[1];
};
2.2 原理解析
let type = function() {};
Object.prototype.toString.call(type); // "[object Function]"
match(/\s+(\w+)/)[1]; // 匹配的就是空格后面的 Function
关键点:
Object.prototype.toString.call()返回格式:[object Type]- 使用正则表达式
/\s+(\w+)/提取类型名 [1]获取第一个捕获组(类型名)
2.3 执行流程
graph TD
A[调用 getType] --> B[Object.prototype.toString.call]
B --> C[返回 object Type 字符串]
C --> D[正则匹配]
D --> E[提取类型名]
E --> F[返回类型]
三、测试用例
3.1 各种类型测试
let type1 = function() {};
let type2 = undefined;
let type3 = null;
let type4 = {};
let type5 = false;
let type6 = '5';
let type7 = 5;
let type8 = [];
let type9 = Symbol('id');
let type10 = BigInt(10);
let type11 = /test/g;
let type12 = new Date();
let type13 = new Map();
let type14 = new Set();
console.log(getType(type1)); // Function
console.log(getType(type2)); // Undefined
console.log(getType(type3)); // Null
console.log(getType(type4)); // Object
console.log(getType(type5)); // Boolean
console.log(getType(type6)); // String
console.log(getType(type7)); // Number
console.log(getType(type8)); // Array
console.log(getType(type9)); // Symbol
console.log(getType(type10)); // BigInt
console.log(getType(type11)); // RegExp
console.log(getType(type12)); // Date
console.log(getType(type13)); // Map
console.log(getType(type14)); // Set
四、增强版本
4.1 支持类型检查函数
// 获取类型
const getType = (type) => {
return Object.prototype.toString.call(type).match(/\s+(\w+)/)[1];
};
// 类型检查函数
const isType = (value, type) => {
return getType(value).toLowerCase() === type.toLowerCase();
};
// 测试
console.log(isType(23, 'number')); // true
console.log(isType('愚公上岸说', 'string')); // true
console.log(isType([], 'array')); // true
console.log(isType({}, 'object')); // true
console.log(isType(null, 'null')); // true
4.2 完整工具函数
class TypeChecker {
// 获取类型
static getType(value) {
return Object.prototype.toString.call(value).match(/\s+(\w+)/)[1];
}
// 类型检查
static isType(value, type) {
return this.getType(value).toLowerCase() === type.toLowerCase();
}
// 具体类型判断方法
static isString(value) {
return this.isType(value, 'string');
}
static isNumber(value) {
return this.isType(value, 'number') && !isNaN(value);
}
static isArray(value) {
return this.isType(value, 'array');
}
static isObject(value) {
return this.isType(value, 'object') && value !== null;
}
static isFunction(value) {
return this.isType(value, 'function');
}
static isNull(value) {
return this.isType(value, 'null');
}
static isUndefined(value) {
return this.isType(value, 'undefined');
}
static isBoolean(value) {
return this.isType(value, 'boolean');
}
static isSymbol(value) {
return this.isType(value, 'symbol');
}
static isBigInt(value) {
return this.isType(value, 'bigint');
}
static isDate(value) {
return this.isType(value, 'date');
}
static isRegExp(value) {
return this.isType(value, 'regexp');
}
static isMap(value) {
return this.isType(value, 'map');
}
static isSet(value) {
return this.isType(value, 'set');
}
static isPromise(value) {
return this.isType(value, 'promise') ||
(this.isObject(value) &&
typeof value.then === 'function' &&
typeof value.catch === 'function');
}
}
// 使用示例
console.log(TypeChecker.isString('hello')); // true
console.log(TypeChecker.isNumber(123)); // true
console.log(TypeChecker.isArray([1, 2, 3])); // true
console.log(TypeChecker.isObject({})); // true
console.log(TypeChecker.isNull(null)); // true
五、特殊类型判断
5.1 判断是否为纯对象
function isPlainObject(value) {
if (TypeChecker.getType(value) !== 'Object') {
return false;
}
// 检查是否有原型
if (Object.getPrototypeOf(value) === null) {
return true;
}
// 检查原型链
let proto = value;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(value) === proto;
}
// 测试
console.log(isPlainObject({})); // true
console.log(isPlainObject(new Object())); // true
console.log(isPlainObject([])); // false
console.log(isPlainObject(null)); // false
5.2 判断是否为类数组
function isArrayLike(value) {
if (value == null || typeof value === 'function') {
return false;
}
const length = value.length;
return typeof length === 'number' &&
length >= 0 &&
length < Number.MAX_SAFE_INTEGER;
}
// 测试
console.log(isArrayLike([1, 2, 3])); // true
console.log(isArrayLike('hello')); // true
console.log(isArrayLike({ length: 3 })); // true
console.log(isArrayLike({ length: -1 })); // false
console.log(isArrayLike(null)); // false
5.3 判断是否为空值
function isEmpty(value) {
if (value == null) {
return true;
}
if (TypeChecker.isArray(value) || TypeChecker.isString(value)) {
return value.length === 0;
}
if (TypeChecker.isObject(value)) {
return Object.keys(value).length === 0;
}
if (TypeChecker.isMap(value) || TypeChecker.isSet(value)) {
return value.size === 0;
}
return false;
}
// 测试
console.log(isEmpty(null)); // true
console.log(isEmpty(undefined)); // true
console.log(isEmpty('')); // true
console.log(isEmpty([])); // true
console.log(isEmpty({})); // true
console.log(isEmpty(new Map())); // true
console.log(isEmpty(new Set())); // true
六、实际应用场景
6.1 参数验证
function processData(data) {
if (!TypeChecker.isObject(data)) {
throw new TypeError('data must be an object');
}
if (!TypeChecker.isString(data.name)) {
throw new TypeError('data.name must be a string');
}
if (!TypeChecker.isNumber(data.age)) {
throw new TypeError('data.age must be a number');
}
// 处理数据
return `Name: ${data.name}, Age: ${data.age}`;
}
6.2 类型转换
function safeParse(value, defaultValue) {
if (TypeChecker.isString(value)) {
try {
return JSON.parse(value);
} catch (e) {
return defaultValue;
}
}
return defaultValue;
}
6.3 深拷贝类型判断
function deepClone(value) {
const type = TypeChecker.getType(value);
if (type === 'Array') {
return value.map(item => deepClone(item));
}
if (type === 'Object') {
const cloned = {};
for (let key in value) {
if (value.hasOwnProperty(key)) {
cloned[key] = deepClone(value[key]);
}
}
return cloned;
}
if (type === 'Date') {
return new Date(value.getTime());
}
if (type === 'RegExp') {
return new RegExp(value.source, value.flags);
}
return value;
}
七、面试要点总结
核心知识点
- Object.prototype.toString:最准确的类型判断方法
- 正则提取:使用正则表达式提取类型名
- 类型检查:提供便捷的类型检查函数
- 特殊类型:处理纯对象、类数组等特殊情况
常见面试题
Q1: 如何准确判断 JavaScript 中的类型?
答:使用 Object.prototype.toString.call() 方法,它返回 [object Type] 格式的字符串,可以准确判断所有类型。
Q2: typeof 和 Object.prototype.toString 的区别?
答:
typeof:简单快速,但无法区分对象类型(数组、日期等)Object.prototype.toString:最准确,可以区分所有类型
Q3: 如何判断一个值是否为数组?
答:
Array.isArray(value)(推荐)Object.prototype.toString.call(value) === '[object Array]'value instanceof Array(不推荐,跨框架可能失效)
实战建议
- ✅ 理解各种类型判断方法的优缺点
- ✅ 掌握
Object.prototype.toString的使用 - ✅ 在实际项目中封装类型判断工具函数
- ✅ 注意特殊类型的处理(null、undefined、NaN 等)
前端面试小册 文章被收录于专栏
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!