声明:本文内容经AI辅助优化,由人工审核编辑,确保技术示例准确可运行。
更新说明:内容适用于现代JavaScript开发。
引言
JavaScript 作为一门动态类型语言,类型判断是日常开发中不可或缺的操作。然而,null 和 undefined 的特殊行为,以及隐式类型转换的复杂性,常常让开发者感到困惑。系统讲解 JavaScript 中的类型判断方法,从基础到高级,帮助你写出更健壮的代码。
基础概念
null vs undefined
1 2 3 4 5 6 7 8 9 10 11 12 13
| let a; console.log(a); console.log(typeof a);
let b = null; console.log(b); console.log(typeof b);
console.log(null == undefined); console.log(null === undefined);
|
两者区别:
| 特性 |
undefined |
null |
| 含义 |
未初始化 |
空值 |
| typeof |
“undefined” |
“object” |
| 转换为数字 |
NaN |
0 |
| 使用场景 |
系统默认 |
主动赋空 |
基础类型判断
1. 判断 undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let a; if (typeof a === "undefined") { console.log("a is undefined"); }
if (a === undefined) { console.log("a is undefined"); }
let b = null; console.log(b == undefined); console.log(b === undefined);
|
最佳实践: 使用 typeof 判断 undefined,避免变量未声明时报错
1 2 3 4 5 6 7 8 9
| if (typeof notDeclaredVar === "undefined") { console.log("变量未声明"); }
if (notDeclaredVar === undefined) { }
|
2. 判断 null
1 2 3 4 5 6 7 8 9 10 11 12 13
| let a = null; if (a === null) { console.log("a is null"); }
if (typeof a === "object" && a === null) { console.log("a is null"); }
console.log(typeof null);
|
3. 同时判断 null 和 undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let value; if (value == null) { console.log("value is null or undefined"); }
let result = value ?? "default";
let result2 = value || "default";
if (value === null || value === undefined) { console.log("value is null or undefined"); }
|
高级类型检测
1. 完整类型判断工具函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| const TypeChecker = { isUndefined(value) { return typeof value === "undefined"; },
isNull(value) { return value === null; },
isNullOrUndefined(value) { return value == null; },
isString(value) { return typeof value === "string"; },
isNumber(value) { return typeof value === "number" && !isNaN(value); },
isBoolean(value) { return typeof value === "boolean"; },
isSymbol(value) { return typeof value === "symbol"; },
isBigInt(value) { return typeof value === "bigint"; },
isFunction(value) { return typeof value === "function"; },
isObject(value) { return typeof value === "object" && value !== null; },
isArray(value) { return Array.isArray(value); },
isPlainObject(value) { return Object.prototype.toString.call(value) === "[object Object]"; },
isDate(value) { return value instanceof Date; },
isRegExp(value) { return value instanceof RegExp; },
isError(value) { return value instanceof Error; },
isPromise(value) { return value instanceof Promise || (this.isObject(value) && typeof value.then === "function"); },
isNaN(value) { return Number.isNaN(value); },
isInfinity(value) { return value === Infinity || value === -Infinity; },
isFinite(value) { return Number.isFinite(value); },
isEmpty(value) { if (this.isNullOrUndefined(value)) return true; if (this.isString(value) || this.isArray(value)) { return value.length === 0; } if (this.isPlainObject(value)) { return Object.keys(value).length === 0; } return false; } };
console.log(TypeChecker.isNullOrUndefined(null)); console.log(TypeChecker.isPlainObject({})); console.log(TypeChecker.isPlainObject([])); console.log(TypeChecker.isEmpty("")); console.log(TypeChecker.isEmpty({}));
|
2. Object.prototype.toString 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function getType(value) { return Object.prototype.toString.call(value).slice(8, -1); }
console.log(getType(undefined)); console.log(getType(null)); console.log(getType(123)); console.log(getType("abc")); console.log(getType(true)); console.log(getType([])); console.log(getType({})); console.log(getType(new Date())); console.log(getType(/abc/)); console.log(getType(new Map())); console.log(getType(new Set())); console.log(getType(function(){}));
|
3. instanceof 运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Animal {} class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); console.log(dog instanceof Animal); console.log(dog instanceof Object);
const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const iframeArray = iframe.contentWindow.Array; console.log([] instanceof iframeArray);
console.log(Object.prototype.toString.call([]) === "[object Array]");
|
实际应用场景
1. 安全的属性访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function getDeepValue(obj, path, defaultValue = undefined) { if (TypeChecker.isNullOrUndefined(obj)) return defaultValue;
const keys = path.split('.'); let current = obj;
for (const key of keys) { if (TypeChecker.isNullOrUndefined(current[key])) { return defaultValue; } current = current[key]; }
return current; }
const user = { profile: { name: "John", address: { city: "Beijing" } } };
console.log(getDeepValue(user, "profile.name")); console.log(getDeepValue(user, "profile.address.city")); console.log(getDeepValue(user, "profile.phone", "N/A")); console.log(getDeepValue(null, "profile.name", "N/A"));
|
2. 表单验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class Validator { static required(value) { if (TypeChecker.isNullOrUndefined(value)) return false; if (TypeChecker.isString(value) && value.trim() === "") return false; if (TypeChecker.isArray(value) && value.length === 0) return false; return true; }
static isEmail(value) { if (!TypeChecker.isString(value)) return false; return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); }
static isPhone(value) { if (!TypeChecker.isString(value) && !TypeChecker.isNumber(value)) { return false; } return /^1[3-9]\d{9}$/.test(String(value)); }
static isInteger(value) { return TypeChecker.isNumber(value) && Number.isInteger(value); }
static inRange(value, min, max) { return TypeChecker.isNumber(value) && value >= min && value <= max; } }
const formData = { name: "John", email: "john@example.com", age: 25 };
console.log(Validator.required(formData.name)); console.log(Validator.isEmail(formData.email)); console.log(Validator.inRange(formData.age, 18, 60));
|
3. 类型转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| const TypeConverter = { toString(value, defaultValue = "") { if (TypeChecker.isNullOrUndefined(value)) return defaultValue; return String(value); },
toNumber(value, defaultValue = 0) { if (TypeChecker.isNullOrUndefined(value)) return defaultValue; const num = Number(value); return isNaN(num) ? defaultValue : num; },
toBoolean(value) { if (!value) return false; if (TypeChecker.isString(value)) { return !["false", "0", "", "null", "undefined"].includes(value.toLowerCase()); } return true; },
toArray(value) { if (TypeChecker.isNullOrUndefined(value)) return []; if (TypeChecker.isArray(value)) return value; return [value]; },
toDate(value) { if (TypeChecker.isDate(value)) return value; if (TypeChecker.isNumber(value) || TypeChecker.isString(value)) { const date = new Date(value); return isNaN(date.getTime()) ? null : date; } return null; } };
console.log(TypeConverter.toNumber("123")); console.log(TypeConverter.toNumber("abc", 0)); console.log(TypeConverter.toBoolean("false")); console.log(TypeConverter.toArray("item"));
|
常见陷阱与解决方案
1. typeof null === “object”
1 2 3 4 5 6 7 8 9 10 11
| console.log(typeof null);
function isNull(value) { return value === null; }
function isObject(value) { return typeof value === "object" && value !== null; }
|
2. NaN 的判断
1 2 3 4 5 6 7
| console.log(NaN === NaN);
console.log(Number.isNaN(NaN)); console.log(isNaN("abc")); console.log(Number.isNaN("abc"));
|
3. 数组的类型判断
1 2 3 4 5 6
| console.log(typeof []);
console.log(Array.isArray([])); console.log(Object.prototype.toString.call([]));
|
4. 构造函数判断的局限
1 2 3 4 5 6 7 8 9 10
| const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const IframeArray = iframe.contentWindow.Array;
const arr = new IframeArray();
console.log(arr instanceof Array); console.log(Array.isArray(arr)); console.log(Object.prototype.toString.call(arr));
|
最佳实践总结
1. 优先使用严格相等
1 2 3 4 5
| if (value === null || value === undefined) { }
if (value == null) { }
|
2. 使用专用方法判断对象类型
1 2 3 4 5 6 7 8
| Array.isArray(value); Number.isNaN(value); Object.prototype.toString.call(value);
value instanceof Array; isNaN(value);
|
3. 防御性编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function safeExecute(fn, defaultValue = undefined) { return function(...args) { try { if (!TypeChecker.isFunction(fn)) { return defaultValue; } return fn.apply(this, args); } catch (error) { console.error("Execution error:", error); return defaultValue; } }; }
const safeParse = safeExecute(JSON.parse, {}); console.log(safeParse('{"a":1}')); console.log(safeParse('invalid'));
|
总结
JavaScript 类型判断的关键点:
- undefined 判断:使用
typeof value === "undefined"
- null 判断:使用
value === null
- null 和 undefined 同时判断:使用
value == null 或显式判断
- 对象类型判断:使用
Object.prototype.toString
- 数组判断:使用
Array.isArray
- NaN 判断:使用
Number.isNaN
掌握这些类型判断方法,可以写出更健壮、更可靠的 JavaScript 代码。