检测未定义的对象属性
如何检查 JavaScript 中的对象属性是否未定义?
检查属性值是否为特殊值
undefined
的常用方法是:
if(o.myProperty === undefined) {
alert("myProperty value is the special value `undefined`");
}
检查对象是否实际上没有这样的属性,因此在尝试访问它时默认返回
undefined
:
if(!o.hasOwnProperty('myProperty')) {
alert("myProperty does not exist");
}
检查与标识符关联的值是否为特殊值
undefined
,
或者
该标识符是否尚未声明:
if(typeof myVariable === 'undefined') {
alert('myVariable is either the special value `undefined`, or it has not been declared');
}
注意:最后一种方法是引用
未声明
标识符而不会出现早期错误的唯一方法,这与具有
undefined
值不同。
在 ECMAScript 5 之前的 JavaScript 版本中,全局对象上名为“undefined”的属性是可写的,因此只需简单检查
foo ===如果意外重新定义,undefined
可能会出现意外行为。在现代 JavaScript 中,该属性是只读的。
但是,在现代 JavaScript 中,“undefined”不是关键字,因此函数内的变量可以命名为“undefined”并遮蔽全局属性。
如果您担心这种(不太可能的)极端情况,可以使用
void 运算符
获取特殊的
undefined
值本身:
if(myVariable === void 0) {
alert("myVariable is the special value `undefined`");
}
我认为这个主题有很多错误的答案。与普遍看法相反,“undefined” 不是 JavaScript 中的关键字,实际上可以为其分配值。
正确代码
执行此测试的最可靠方法是:
if (typeof myVar === "undefined")
这将始终返回正确的结果,甚至可以处理未声明
myVar
的情况。
退化代码。请勿使用。
var undefined = false; // Shockingly, this is completely legal!
if (myVar === undefined) {
alert("You have been misled. Run away!");
}
此外,
myVar === undefined
将在未声明 myVar 的情况下引发错误。
这里的许多答案都极力推荐
typeof
,但
typeof
是一个糟糕的选择
。它
永远
不应用于检查变量是否具有值
undefined
,因为它充当了对值
undefined
和变量是否存在的综合检查。在绝大多数情况下,您知道变量何时存在,如果您在变量名称或字符串文字
'undefined'
中输入错误,
typeof
只会引入静默失败的可能性。
var snapshot = …;
if (typeof snaposhot === 'undefined') {
// ^
// misspelled¹ – this will never run, but it won’t throw an error!
}
var foo = …;
if (typeof foo === 'undefned') {
// ^
// misspelled – this will never run, but it won’t throw an error!
}
因此,除非您正在进行功能检测²,其中不确定给定名称是否在范围内(例如检查
typeof module !== 'undefined'
作为特定于 CommonJS 环境的代码中的步骤),
typeof
在变量上使用时是一种有害的选择,正确的选择是直接比较值:
var foo = …;
if (foo === undefined) {
⋮
}
关于此的一些常见误解包括:
-
读取“未初始化”的变量(
var foo
)或参数(function bar(foo) { … },称为
bar()
)将失败。这根本不是真的——没有显式初始化的变量和未赋值的参数始终变为undefined
,并且始终在范围内。 -
可以覆盖
undefined
。确实undefined
不是关键字,但它 是 只读且不可配置的。尽管其他内置函数不是关键字状态(Object
、Math
、NaN
……),但您可能不会避免使用这些内置函数,并且实际代码通常不是在主动恶意环境中编写的,因此这不是担心undefined
的好理由。 (但如果您正在编写代码生成器,请随意使用void 0
。)
了解了变量的工作方式后,是时候解决实际问题了:对象属性。没有理由对对象属性使用
typeof
。先前关于特性检测的例外不适用于此处 -
typeof
仅对变量具有特殊行为,引用对象属性的表达式不是变量。
这个:
if (typeof foo.bar === 'undefined') {
⋮
}
始终 与这个³:
if (foo.bar === undefined) {
⋮
}
完全等同于
if (foo.bar === undefined) {
⋮
}
并且考虑到上述建议,为了避免使读者对您使用
typeof
的原因感到困惑,因为使用
===
检查相等性是最有意义的,因为它可以重构为稍后检查变量的值,并且因为它看起来更好,
您也应该始终在这里使用
=== undefined
³
。
当涉及到对象属性时,还需要考虑的另一件事是您是否真的想要检查
undefined
。给定的属性名称可以不存在于对象上(读取时生成值
undefined
),可以存在于对象本身上,值为
undefined
,可以存在于对象的原型上,值为
undefined
,或者存在于上述任一对象上,但值为非
undefined
。
'key' in obj
将告诉您键是否位于对象原型链的任何位置,而
Object.prototype.hasOwnProperty.call(obj, 'key')
将告诉您它是否直接位于对象上。不过,在这个答案中,我不会详细介绍原型和使用对象作为字符串键映射,因为这主要是为了反驳其他答案中的所有错误建议,而不管原始问题的可能解释如何。阅读
MDN 上的对象原型
以了解更多信息!
¹示例变量名称的不寻常选择?这是Firefox的NoScript扩展的真实死亡代码。
²不认为不知道范围中的内容一般是可以的。滥用动态范围引起的奖励脆弱性:
项目零1225
再次假设ES5+环境,该
undefined
是指全局对象的
不确定的
属性。