如何在JavaScript中检查“undefined”?

3036

在JavaScript中,测试变量是否未定义的最合适方法是什么?

我看到了几种可能的方式:

if (window.myVariable)
或者
if (typeof(myVariable) != "undefined")
或。
if (myVariable) // This throws an error if undefined. Should this be in Try/Catch?

9
你想要检查的是仅仅 undefined,还是包括 null - Nick Craver
4
请查看此链接:https://dev59.com/PnVD5IYBdhLWcg3wTJrF。 - Amr Badawy
7
@Robert - 那个问题已经有一个被接受的答案,证明这里的回答都是错误的。 - Daniel Schaffer
3
“Duplicate”是关于对象属性的,因此一些答案并不适用于这个关于变量的问题。 - DCShannon
显示剩余6条评论
16个回答

3185

如果你想找出一个变量是否被声明,而不管它的值如何,那么使用 in 运算符是最安全的方法。考虑以下示例:

// global scope
var theFu; // theFu has been declared, but its value is undefined
typeof theFu; // "undefined"

但是在某些情况下,这可能不是预期的结果,因为变量或属性已经声明但未初始化。使用in运算符进行更强大的检查。

"theFu" in window; // true
"theFoo" in window; // false
如果你想知道变量是否未定义或是否具有值undefined,那么请使用typeof运算符,它保证返回一个字符串:

如果你想知道变量是否未定义或是否具有值undefined,那么请使用typeof运算符,它保证返回一个字符串:

if (typeof myVar !== 'undefined')

undefined进行直接比较是有问题的,因为undefined可能被覆盖。

window.undefined = "foo";
"foo" == undefined // true

正如@CMS所指出的,这个问题已在ECMAScript第5版中修复,undefined是不可写的。

if (window.myVar)也会包括这些falsy值,所以它不太健壮:

false
0
""
NaN
null
undefined

感谢@CMS指出你第三种情况 - if(myVariable) 在两种情况下也可能引发错误。第一种情况是变量未被定义,这会引发一个ReferenceError

// abc was never declared.
if (abc) {
    // ReferenceError: abc is not defined
} 

另一种情况是变量已经被定义,但有一个getter函数在调用时会抛出错误。例如:

// or it's a property that can throw an error
Object.defineProperty(window, "myVariable", { 
    get: function() { throw new Error("W00t?"); }, 
    set: undefined 
});
if (myVariable) {
    // Error: W00t?
}

9
@Anurag,如果myVariable没有被声明,第三种情况会抛出一个ReferenceError - Christian C. Salvadó
6
@Anurag,欢迎您。由于您提到了ES5,也许值得一提的是undefined现在被描述为不可写、不可配置和不可枚举的。因此,在严格模式下,window.undefined = "omg";将会静默失败或抛出错误。 - Christian C. Salvadó
9
“typeof”可以被重新定义吗? - makerofthings7
7
typeof是一种语言声明,它不能被重新定义,就像if/else/while/for/function等语句一样。 - MooGoo
74
在现代浏览器中,undefined 是不可变的。设置 window.undefined 不会产生任何效果。 - Paul S.
显示剩余22条评论

1537

我个人使用

myVar === undefined

警告:请注意使用===而不是==,并且myVar已经被先前声明(而非定义)。


我不喜欢typeof myVar === "undefined"。我认为它太啰嗦,没有必要。(我可以用更少的代码实现同样的功能。)

现在有些人会阅读这篇文章感到痛苦并尖叫:“等等!等等!!!undefined可以被重新定义!”

没错,我知道这一点。但是,Javascript 中的大多数变量都可以被重新定义。难道你永远不应该使用任何可以被重新定义的内置标识符吗?

如果你遵循这个规则,那就很好,你不是伪君子。

问题是,在JS中做许多真正的工作时,开发人员需要依赖于可重新定义的标识符。我没有听到有人告诉我不应该使用setTimeout,因为某个人可以......

window.setTimeout = function () {
    alert("Got you now!");
};

总之,使用“可以重新定义”的论点来避免使用原始的 === undefined 是不可信的。

(如果您仍然担心 undefined 被重新定义,那么为什么要盲目地将未经测试的库代码集成到您的代码库中呢?或者更简单:使用一个linting工具。)


此外,像 typeof 方法一样,这种技术可以“检测”未声明的变量:

if (window.someVar === undefined) {
    doSomething();
}

但是这两种技术在其抽象化方面存在泄漏。我建议您不要使用这个或者

if (typeof myVar !== "undefined") {
    doSomething();
}

考虑:

var iAmUndefined;

为了判断变量是否已声明,你可能需要使用in运算符。(在许多情况下,您只需读取代码 O_o)。

if ("myVar" in window) {
    doSomething();
}

但是!还有更多!如果发生原型链魔法呢?现在即使是优秀的in运算符也不足够了。(好了,我关于这部分讲完了,除了说99%的时间,=== undefined(和 ****咳咳**** typeof)就可以正常工作。如果你真的在意,你可以自己阅读关于这个主题的文章。)


59
由于人们在进行检查时确实使用undefined,因此重新定义undefined的可能性略微增加。有些人习惯性地将常量放在左侧进行此类检查:if (undefined == someVariable)。只需要一个打字错误就会悄悄地重新定义undefinedif (undefined = someVariable) - Tim Down
62
我从不编写将 undefined 赋值给左手边变量的代码。即便我犯了这个错误,我使用 === 而非 == 也能大大降低出错概率。但是,使用 == 的确是一个更加令人担忧的问题。无论如何,像这样的 Bug 通常很容易被发现。就像这个 Bug:typeof x == "undefned" - Thomas Eding
45
这个怎么可能得到41个赞,它根本不起作用。如果myVar确实未定义,代码将会抛出错误,而且很容易进行测试 - http://jsfiddle.net/WcM5g/ 正确的方式是 typeof myVar === 'undefined' - laurent
44
@Laurent: 这是个笑话吧?这假设变量以某种方式被声明,比如使用 var 关键字或函数参数。在任何情况下,我都不会有意编写尝试操作未声明变量的代码。请记住,在 JS 中,未声明和未定义是两个不同的概念。 - Thomas Eding
17
在C(和C ++)中,颠倒操作数是常见且良好的实践,以避免拼写错误。if(NULL = myVar)无法编译并立即被捕获,而if(myVar = NULL)会创建一个可能难以跟踪的错误,具体取决于周围的其他代码。现代编译器应该会给出警告,但许多经验丰富的C程序员已经养成了交换顺序的习惯。 - GrandOpener
显示剩余20条评论

373

2020更新

我之前更喜欢使用 typeof 进行检查的原因之一(即 undefined 可以被重新定义),随着 ECMAScript 5 的普及已经不再具有意义。另一个原因是你可以使用 typeof 来检查未声明变量的类型,但这个情况一直比较少见。因此,我现在建议在大多数情况下使用直接比较:

myVariable === undefined

2010年的原始回答

我更喜欢使用typeof。它可以在变量从未被声明时起作用,而与=====运算符的任何比较或使用if进行类型强制转换不同(undefinednull不同,在 ECMAScript 3 环境中可能也被重新定义,因此不能保证其可靠性,但几乎所有常见环境现在都符合ECMAScript 5或以上)。

if (typeof someUndeclaredVariable == "undefined") {
    // Works
}

if (someUndeclaredVariable === undefined) { 
    // Throws an error
}

20
您可能需要检查特定的全局变量是否已经被定义来表示某个功能。例如,库代码可能希望检查该库是否已经被先前包含过。 - Tim Down
1
窗口中的'xyz'或自身中的'xyz'更好。 - Jamie Pate
5
为了明确,我不同意'xyz' in windowtypeof xyz == "undefined"更好,因为它检查的是错误的内容。in运算符检查属性是否存在,而不管其值如何,而问题至少似乎在询问如何测试变量的值是否为“undefined”。也许我选择的一个更好的例子应该是var foo; "foo" in window;这会返回true,而foo肯定是未定义的。 - Tim Down
1
@JamiePate:为什么 xyz === undefinedtypeof xyz == "undefined" 更好?我同意关于全局变量的问题,但我们两个人中只有你一直在推荐检查 window 的属性。 - Tim Down
6
大多数情况下这是多余的(也更不易读)。如果您知道xyz是一个已声明的变量,为什么要增加额外的麻烦呢?在某些浏览器中,类型检查和字符串比较速度较慢,因此如果您在一个紧密循环中经常这样做,会降低一些性能。http://jsperf.com/type-of-undefined-vs-undefined/6 - Jamie Pate
显示剩余4条评论

126
您可以使用typeof,像这样:
if (typeof something != "undefined") {
    // ...
}

10
假设您已经采取预防措施,定义了变量undefined,那么您可以使用something !== undefined来判断。 - James
4
很高兴看到你现在加了引号。然而,正如我在我的答案中所提到的,需要注意的是,在这种情况下不必使用严格比较(!==),因为 typeof 总是会返回一个字符串。 - Mathias Bynens
16
使用严格比较还是非严格比较是个人口味问题,两者都能正常工作,也没有哪一个更正确。这可能取决于你的默认立场是始终使用严格比较,除非明确需要类型转换(例如Crockford建议的方式),还是倾向于在需要严格性时才使用严格比较,除此之外使用非严格比较。 - Tim Down
2
这是不准确的。你绝对不需要使用 typeof - Thomas Eding
1
哦,现在我明白你的意思了;你的评论有误导性,因为它看起来与代码的正确性有关。是的,没有必要使用它,因为一切都取决于个人口味;如果你知道你在做什么,你甚至不需要对用户输入进行消毒;但这并不意味着不应该进行消毒。在这种情况下,在所有答案中,使用 typeof 是最安全和最不容易出错的选项。与其写这样令人困惑的评论,我倒不如编辑答案,换用另一个术语代替“需要”。例如像“你可以|应该|最好|可能使用 typeof”这样的说法 :) - Kamafeather
显示剩余2条评论

75

更新于2018年7月25日

自从发布这篇文章已近五年,JavaScript 已经迈进了很长的一步。在重复原始文章中的测试时,我发现以下测试方法之间没有一致的差异:

  • abc === undefined
  • abc === void 0
  • typeof abc == 'undefined'
  • typeof abc === 'undefined'

即使我修改测试以防止 Chrome 将其优化掉,差异也微不足道。因此,我现在建议使用 abc === undefined 来提高清晰度。

来自 chrome://version 的相关内容:

  • Google Chrome:67.0.3396.99(Official Build)(64 位)(cohort: Stable)
  • 修订版:a337fbf3c2ab8ebc6b64b0bfdce73a20e2e2252b-refs/branch-heads/3396@{#790}
  • 操作系统:Windows
  • JavaScript:V8 6.7.288.46
  • 用户代理:Mozilla/5.0 (Windows NT 10.0;Win64;x64)AppleWebKit/537.36(KHTML, like Gecko)Chrome/67.0.3396.99 Safari/537.36

原始文章于2013年11月1日发布

在 Google Chrome 中,以下方法比使用 typeof 测试略微快一些:

if (abc === void 0) {
    // Undefined
}

差异可以忽略不计。但是,对于了解 void 0 含义的人来说,这段代码更加简洁、一目了然。请注意,abc 仍然需要被声明。

typeofvoid 都比直接与 undefined 进行比较要快得多。我在 Chrome 开发者控制台中使用了以下测试格式:

var abc;
start = +new Date();
for (var i = 0; i < 10000000; i++) {
    if (TEST) {
        void 1;
    }
}
end = +new Date();
end - start;

结果如下:

Test: | abc === undefined      abc === void 0      typeof abc == 'undefined'
------+---------------------------------------------------------------------
x10M  |     13678 ms               9854 ms                 9888 ms
  x1  |    1367.8 ns              985.4 ns                988.8 ns
请注意,第一行是以毫秒为单位的,而第二行是以纳秒为单位的。3.4纳秒的差异微不足道。在后续测试中,时间相当稳定。

好心疼,这是-1;我花了很多时间测试它。哦,算了吧。这是有用的信息,所以我会把它留在这里。记住,不要使用“===”来测试“undefined”! - Zenexer
1
我认为-1是因为:1)<q>对于知道void 0的含义的人来说,更加清晰易懂</q>,因为void 0对我来说听起来更不寻常;2)你应该分享你的性能测试,但主要是3)如果abc未定义,则你的第一个示例(abc === void 0)会抛出异常。 - drzaus
我已将你的方法添加到我的测试列表中,并且它确实通过了检查(并不是我怀疑你)-- http://jsfiddle.net/drzaus/UVjM4/8/ - drzaus
我认为在这些数字(这些数字是一段时间以前的)中,最好的权衡清晰度和速度的方法是使用 typeof 测试。 - Zenexer
1
我觉得很惊奇的是未定义的比较速度比void 0慢。我想象运行的JS版本足够新,可以保证undefined是常量。太遗憾了。 - Thomas Eding

28

如果它是未定义的,那么它不会等于包含字符"undefined"的字符串,因为该字符串并不是未定义的。

您可以检查变量的类型:

if (typeof(something) != "undefined") ...
有时候你甚至不需要检查类型。如果变量的值被设置为不能评估为false(例如是一个函数),那么你可以直接评估这个变量。示例:

有时候你甚至不需要检查类型。如果变量的值被设置为不能评估为false(例如是一个函数),那么你可以直接评估这个变量。示例:

if (something) {
  something(param);
}

16
无需使用括号:typeof 是一个操作符,而非函数。 - Tim Down
8
可以双向使用。 - Nick Craver
1
@Tim:@Nick是正确的。请参见https://developer.mozilla.org/en/Core_Javascript_1.5_Reference/Operators/Special_Operators/typeof_Operator。 - Mathias Bynens
21
我知道在这里加上圆括号会让代码“运行”,因为圆括号在这里形成了分组运算符,只是对括号内的操作数进行评估和返回。我只是说这些圆括号是不必要的。 - Tim Down

26
if (typeof foo == 'undefined') {
 // Do something
};

请注意,这种情况下不必使用严格比较运算符(!==),因为 typeof 总是返回一个字符串。


4
分号 (};) 有什么作用? - James
2
@J-P:在右括号后面的分号只是一个空语句。 - Gumbo
3
@Gumbo,抱歉,我本意是要问:“分号起到什么作用?” - James
1
@J-P 这只是个人偏好。我喜欢添加可选的分号 - if块可以被视为一行代码进行重写,然后附加分号就有意义了,因为这是我几乎每个其他语句的结束方式。if (typeof foo == 'undefined') { };此外,这还确保与某些JavaScript缩小器兼容。我知道JSLint不建议这样做,但我只是看不出来有什么意义 - 这些分号是无害的,如果有什么区别,它们会强制执行稍微严格一点的编码风格。 - Mathias Bynens
9
我从未遇到过不能处理if(){}而需要加;的缩小器...你指的是哪些缩小器呢?你说这是你结束每个其他语句的方式...我想这是正确的。但是,代码块{}本身就是一个语句。加上;会使其成为两个语句,从语法上讲,这是多余的。即使是自动分号插入也不会在那里加上分号... - James
3
@J-P: 我猜我几年前读了Packer文档后开始这样做。Packer在function() {}声明后期望有分号。不过你是对的——显然在if语句后面不需要分号,但不知怎么的我仍觉得加上分号有意义。 - Mathias Bynens

19

以下是各种答案结果的一些场景示例: http://jsfiddle.net/drzaus/UVjM4/

(请注意,当在作用域包装器中使用var进行in测试时会有所不同)

参考代码:

(function(undefined) {
    var definedButNotInitialized;
    definedAndInitialized = 3;
    someObject = {
        firstProp: "1"
        , secondProp: false
        // , undefinedProp not defined
    }
    // var notDefined;

    var tests = [
        'definedButNotInitialized in window',
        'definedAndInitialized in window',
        'someObject.firstProp in window',
        'someObject.secondProp in window',
        'someObject.undefinedProp in window',
        'notDefined in window',

        '"definedButNotInitialized" in window',
        '"definedAndInitialized" in window',
        '"someObject.firstProp" in window',
        '"someObject.secondProp" in window',
        '"someObject.undefinedProp" in window',
        '"notDefined" in window',

        'typeof definedButNotInitialized == "undefined"',
        'typeof definedButNotInitialized === typeof undefined',
        'definedButNotInitialized === undefined',
        '! definedButNotInitialized',
        '!! definedButNotInitialized',

        'typeof definedAndInitialized == "undefined"',
        'typeof definedAndInitialized === typeof undefined',
        'definedAndInitialized === undefined',
        '! definedAndInitialized',
        '!! definedAndInitialized',

        'typeof someObject.firstProp == "undefined"',
        'typeof someObject.firstProp === typeof undefined',
        'someObject.firstProp === undefined',
        '! someObject.firstProp',
        '!! someObject.firstProp',

        'typeof someObject.secondProp == "undefined"',
        'typeof someObject.secondProp === typeof undefined',
        'someObject.secondProp === undefined',
        '! someObject.secondProp',
        '!! someObject.secondProp',

        'typeof someObject.undefinedProp == "undefined"',
        'typeof someObject.undefinedProp === typeof undefined',
        'someObject.undefinedProp === undefined',
        '! someObject.undefinedProp',
        '!! someObject.undefinedProp',

        'typeof notDefined == "undefined"',
        'typeof notDefined === typeof undefined',
        'notDefined === undefined',
        '! notDefined',
        '!! notDefined'
    ];

    var output = document.getElementById('results');
    var result = '';
    for(var t in tests) {
        if( !tests.hasOwnProperty(t) ) continue; // bleh

        try {
            result = eval(tests[t]);
        } catch(ex) {
            result = 'Exception--' + ex;
        }
        console.log(tests[t], result);
        output.innerHTML += "\n" + tests[t] + ": " + result;
    }
})();

并且结果:

definedButNotInitialized in window: true
definedAndInitialized in window: false
someObject.firstProp in window: false
someObject.secondProp in window: false
someObject.undefinedProp in window: true
notDefined in window: Exception--ReferenceError: notDefined is not defined
"definedButNotInitialized" in window: false
"definedAndInitialized" in window: true
"someObject.firstProp" in window: false
"someObject.secondProp" in window: false
"someObject.undefinedProp" in window: false
"notDefined" in window: false
typeof definedButNotInitialized == "undefined": true
typeof definedButNotInitialized === typeof undefined: true
definedButNotInitialized === undefined: true
! definedButNotInitialized: true
!! definedButNotInitialized: false
typeof definedAndInitialized == "undefined": false
typeof definedAndInitialized === typeof undefined: false
definedAndInitialized === undefined: false
! definedAndInitialized: false
!! definedAndInitialized: true
typeof someObject.firstProp == "undefined": false
typeof someObject.firstProp === typeof undefined: false
someObject.firstProp === undefined: false
! someObject.firstProp: false
!! someObject.firstProp: true
typeof someObject.secondProp == "undefined": false
typeof someObject.secondProp === typeof undefined: false
someObject.secondProp === undefined: false
! someObject.secondProp: true
!! someObject.secondProp: false
typeof someObject.undefinedProp == "undefined": true
typeof someObject.undefinedProp === typeof undefined: true
someObject.undefinedProp === undefined: true
! someObject.undefinedProp: true
!! someObject.undefinedProp: false
typeof notDefined == "undefined": true
typeof notDefined === typeof undefined: true
notDefined === undefined: Exception--ReferenceError: notDefined is not defined
! notDefined: Exception--ReferenceError: notDefined is not defined
!! notDefined: Exception--ReferenceError: notDefined is not defined

1
请注意在作用域包装器中使用 undefined;这不仅可以防止(不寻常的)情况“哦,但是undefined可以被重新定义”,而且还有助于缩小文件大小。 - drzaus

18

我在这篇文章中读到,像Underscore.js这样的框架使用此函数:

function isUndefined(obj){
    return obj === void 0;
}

15

就我个人而言,我总是使用以下内容:

var x;
if( x === undefined) {
    //Do something here
}
else {
   //Do something else here
}

在所有现代浏览器中(JavaScript 1.8.5或更高版本),window.undefined属性都是不可写的。来自Mozilla文档 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined,我看到了这个:使用typeof()的一个原因是,如果变量未被定义,它不会抛出错误。

我倾向于使用

x === undefined 

因为如果x没有被声明,它会在我的脸上失败和爆炸,而不是静默地传递/失败。这会提醒我x没有被声明。我认为JavaScript中使用的所有变量都应该被声明。


您可以使用作用域包装器重新声明undefined(function($, undefined){ /* undefined在这里是'abc' */ })(jQuery, 'abc');,这也是人们抱怨它在技术上不安全的原因,除非您百分之百确定自己的代码在何处运行。 - drzaus
关于希望未声明的变量出现错误的观点非常好 - 这在 typeof 中不会发生。 - FruitBreak
1
随着2021年7月发布的Chrome for Windows(版本92.0.4515.107),我尝试了以下代码: if ( myVar === undefined ), if ( myVar === 'undefined' ), if ( myVar === void 0), or if ( !myVar ) 但是所有情况都失败了!每种情况都抛出了一个未定义的JavaScript错误,并有效地返回了“true”,导致分支被执行... 解决方案:if ( !window.myVar ) myVar = false; 这就是我需要的全部内容,将其声明为全局false,如果以前的库没有包含初始化为0/false。因此,FYI,最佳解决方案将涉及使用window对象! - John Doe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接