JavaScript 中 typeof 运算符的用法、陷阱与替代方案
本文全面解析 JavaScript 中 typeof 运算符的多种用法及常见陷阱。重点讨论如何利用 typeof 安全检测未声明变量,深入剖析 typeof null 返回 'object' 的历史原因及解决方案。此外,还介绍了区分对象与原始值、跨框架函数检测等高级场景,并提供了 isDefined、isObject 等替代函数的实现思路,帮助开发者更准确地处理类型判断。
typeof 运算符概述
JavaScript 中的 typeof 运算符功能丰富,应用场景广泛,但也存在不少令人困惑的行为。本文将系统梳理其多种用法,并指出其中存在的问题及相应的解决策略。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FOperators%2Ftypeof
> typeof undefined 'undefined' > typeof null // well-known bug 'object' > typeof true 'boolean' > typeof 123 'number' > typeof "abc" 'string' > typeof function() {} 'function' > typeof {} 'object' > typeof [] 'object' > typeof unknownVariable 'undefined'
检测变量存在性与值
1. 检查变量是否存在及其值。 typeof 在两种情况下会返回 "undefined":一是变量未被声明,二是变量的值为 undefined。例如:
> typeof undeclaredVariable === "undefined" true > var declaredVariable; > typeof declaredVariable 'undefined' > typeof undefined 'undefined' 还有其他方法可以检测某个值是否为 undefined:
> var value = undefined; > value === undefined true 然而,如果直接对未声明的变量使用等号比较,会抛出异常。只有 typeof 能够在不报错的情况下正常检测未声明的变量:
> undeclaredVariable === undefined ReferenceError: undeclaredVariable is not defined 需要注意的是,未初始化的变量、未传入参数的形参以及不存在的属性,都不会出现上述问题,因为它们始终可访问且值默认为 undefined:
> var declaredVariable; > declaredVariable === undefined true > (function (x) { return x === undefined }()) true > ({}).foo === undefined true 译者注:因此,如果想检测一个可能未被声明的全局变量是否存在,也可以使用 if(window.maybeUndeclaredVariable){}
问题:typeof 在执行此类任务时显得较为繁琐。
解决办法:这类操作并不常见,因此有人认为无需寻找更优方案。不过,或许有人希望引入专门的操作符:
> defined undeclaredVariable false > var declaredVariable; > defined declaredVariable false 或者,也许还需要一个检测变量是否已声明的操作符:
> declared undeclaredVariable false > var declaredVariable; > declared declaredVariable true 译者注:在 perl 里,上面的 defined 操作符相当于 defined(),上面的 declared 操作符相当于 exists(),
处理 null 与 undefined 的判断陷阱
2. 判断值是否不等于 undefined 也不等于 null。 问题:如果你想检测一个值是否已被定义(即值不是 undefined 也不是 null),那么你会遇到 typeof 最著名的怪异表现(常被视为 bug):typeof null 返回了 "object":
> typeof null 'object' 译者注:这只能说是最初 JavaScript 实现的 bug,而现在标准已如此规范。V8 曾经修正并实现过 typeof null === "null",但最终证明不可行.http://wiki.ecmascript.org/doku.php?id=harmony:typeof_null
解决办法:不要使用 typeof 来做这项任务,改用以下函数代替:
function isDefined(x) { return x !== null && x !== undefined; } 另一种可能性是引入一个“默认值运算符”,在 myValue 未定义的情况下,下面的表达式会返回 defaultValue:
myValue ?? defaultValue 上面的表达式等价于:
(myValue !== undefined && myValue !== null) ? myValue : defaultValue 又或者:
myValue ??= defaultValue 其实是下面这条语句的简化:
myValue = myValue ?? defaultValue 当你访问嵌套属性时,比如 bar,你或许需要这个运算符的帮助:
obj.foo.bar 如果 obj 或者 obj.foo 是未定义的,上面的表达式会抛出异常。一个运算符.?? 可以让上面的表达式在遍历一层层属性时,返回第一个遇到的值为 undefined 或 null 的属性:
obj.??foo.??bar 上面的表达式等价于:
(obj === undefined || obj === null) ? obj : (obj.foo === undefined || obj.foo === null) ? obj.foo : obj.foo.bar
区分对象与原始值
3. 区分对象值和原始值。 下面的函数用来检测 x 是否是一个对象值:
function isObject(x) { return (typeof x === "function" || (typeof x === "object" && x !== null)); } 问题:上面的检测比较复杂,是因为 typeof 把函数和对象看成是不同的类型,而且 typeof null 返回 "object"。
解决办法:下面的方法也常用于检测对象值:
function isObject2(x) { return x === Object(x); } 警告:你也许认为这里可以使用 instanceof Object 来检测,但是 instanceof 是通过使用一个对象的原型来判断实例关系的,那么没有原型的对象怎么办呢:
> var obj = Object.create(null); > Object.getPrototypeOf(obj) null obj 确实是一个对象,但它不是任何值的实例:
> typeof obj 'object' > obj instanceof Object false 在实际中,你可能很少遇到这样的对象,但它的确存在,而且有它的用途。
译者注:Object.prototype 就是一个默认存在的,没有原型的对象
>Object.getPrototypeOf(Object.prototype)null>typeof Object.prototype'object'>Object.prototype instanceof Object false
获取原始值类型与函数检测
4. 原始值的类型是什么? typeof 是查看某个原始值类型的最佳方式。
> typeof "abc" 'string' > typeof undefined 'undefined' 问题:你必须知道 typeof null 的怪异表现。
> typeof null // 要小心! 'object' 解决办法:下面的函数可以修复这个问题(只针对这个用例)。
function getPrimitiveTypeName(x) { var typeName = typeof x; switch(typeName) { case "undefined": case "boolean": case "number": case "string": return typeName; case "object": if (x === null) { return "null"; } default: // 前面的判断都没通过 throw new TypeError("参数不是一个原始值: "+x); } } 更好的解决办法:实现一个函数 getTypeName(),除了可以返回原始值的的类型,还可以返回对象值的内部[[Class]]属性.这里讲了如何实现这个函数(译者注:jQuery中的$.type就是这样的实现)
5. 某个值是否是函数 typeof 可以用来检测一个值是否是函数.> typeof function () {} 'function' > typeof Object.prototype.toString 'function' 原则上说,instanceof Function 也可以进行这种需求的检测.乍一看,貌似写法还更加优雅.但是,浏览器有一个怪癖:每一个框架和窗口都有它自己的全局变量.因此,如果你将某个框架中的对象传到另一个框架中,instanceof 就不能正常工作了,因为这两个框架有着不同的构造函数.这就是为什么 ECMAScript5 中会有 Array.isArray() 方法的原因.如果有一个能够跨框架的,用于检查一个对象是否是给定的构造函数的实例的方法的话,那会很好.上述的 getTypeName() 是一个可用的变通方法,但也许还有一个更根本的解决方案。
6. 综述 下面提到的,应该是目前 JavaScript 中最迫切需要的,可以代替一些 typeof 目前职责的功能特性:
isDefined() (比如 Object.isDefined()): 可以作为一个函数或者一个运算符 isObject() getTypeName() 能够跨框架的,检测一个对象是否是指定的构造函数的实例的机制 检查某个变量是否已经被声明这样的需求,可能没那么必要有自己的运算符。
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19
JS对象数组转字符串为什么推荐用JSON.stringify()
2026-11-05 09:40:31
