variable === undefined 与 typeof variable === "undefined" 的区别

372

jQuery核心样式指南建议两种不同的方法来检查变量是否已定义。

  • 全局变量:typeof variable === "undefined"
  • 局部变量:variable === undefined
  • 属性:object.prop === undefined

为什么jQuery在全局变量和局部/属性变量中使用不同的方法?


我无法回答为什么JQuery会同时使用这两种方法,但是Javascript确实有一些有趣的怪癖,这意味着这两个东西略有不同。大多数情况下(即如果您的代码是合理的),这并不重要,但是仍然存在差异:请参见此处的撰写 - http://wtfjs.com/2010/02/15/undefined-is-mutable - Spudley
2
正如@Struppi所指出的,jQuery的最外层函数有一个名为undefined的参数。在jQuery中,foo === undefined是针对本地副本的undefined进行检查,而不是全局的(window.undefined),后者可能已被疯狂的代码修改过。undefined是可变的这一事实确实值得注意,我很高兴你提到了这一点。(+1) - Patrick McElhaney
2
该文章的当前链接为https://wtfjs.com/wtfs/2010-02-15-undefined-is-mutable。 - enigment
9个回答

443

对于未声明的变量,typeof foo会返回字符串字面量"undefined",而恒等式检查foo === undefined会触发错误"foo未定义"

对于本地变量(您知道在某处已经声明了),不会发生这样的错误,因此使用恒等式检查。


4
@goreSplatter 现在你不能删除它了。:-) 选择很困难,但问题的措辞方式,这个答案更加合适。对于任何对 undefined 的工作方式感兴趣的人(就像我一样),也应该看看其他答案,特别是 @Tim 的答案。 - Patrick McElhaney
4
我会在typeof foo; // -> "undefined"中添加引号,以示强调它是一个字符串而不是原始值undefined - c24w
我想补充一下,检查(x !== undefined)也会对本地函数变量失败,如果您在函数中尝试访问它之后声明了“let”或“cont”类型。JavaScript会将它们提升,但它们处于死区,因此即使这样也会失败:function MyError(){ if(x !== undefined){console.log(x);} let x = 1;} MyError(); >>> "Uncaught ReferenceError: Cannot access 'x' before initialization." 这迫使开发人员在顶部声明所有的let/const变量,而不是像天真的开发人员认为他们可以使用(x !== undefined)在本地范围内检查已声明的let。 - Stokely

143

我建议你在任何地方都使用typeof foo === "undefined"。这永远不会出错。

我想jQuery推荐两种不同的方法是因为他们在包含jQuery代码的函数内部定义了自己的undefined变量,所以在该函数内部,undefined是安全的,不会被外部篡改。我还想象过,某个人在某个地方对这两种不同的方法进行了基准测试,并发现foo === undefined更快,因此决定采用这种方法。 [更新:如评论中所指出的,与undefined的比较也稍微更短,这可能是一个考虑因素。] 但在实际情况下,获得的收益将非常微不足道:这种检查永远不会成为任何一种瓶颈,而且你失去的东西是很重要的:评估主机对象的属性进行比较可能会引发错误,而typeof检查永远不会。

例如,在IE中用于解析XML的是以下内容:

var x = new ActiveXObject("Microsoft.XMLDOM");

为了安全地检查一个对象是否有 loadXML 方法:

typeof x.loadXML === "undefined"; // Returns false

另一方面:

x.loadXML === undefined; // Throws an error

更新

我忘记提到的typeof检查的另一个优点是它也适用于未声明的变量,而foo === undefined检查不适用,并且实际上会抛出一个ReferenceError。感谢@LinusKleen提醒我。例如:

typeof someUndeclaredVariable; // "undefined"
someUndeclaredVariable === undefined; // throws a ReferenceError

底线:始终使用typeof检查。


12
谢谢Tim。你关于性能的观点很有道理。jQuery团队可能更关心对文件大小的影响。当代码进行压缩时,foo === undefined 可能会被缩写为 f===u,而 typeof foo === "undefined" 只能被简化为 typeof f==="undefined" - Patrick McElhaney
8
好的观点,但我不确定typeof对未声明变量的安全性是否是一个优势。如果有任何问题,它更容易让拼写错误通过,而且我看不出你实际上想要检查未声明变量的类型的情况。 - David Tang
4
我可以想象在图书馆使用它来检查另一个图书馆是否存在。 - Tim Down
3
这就是不使用JSLint的一个原因。 - Tim Down
1
@JohnKurlak:“can”是关键词。 - Tim Down
显示剩余16条评论

34

使用typeof变量的另一个原因是:undefined可以被重新定义。

undefined = "foo";
var variable = "foo";
if (variable === undefined)
  console.log("eh, what?!");
typeof variable 的结果无法确定。

更新: 请注意,这在 ES5 中不适用,因为全局的 undefined 是一个不可配置、不可写属性:

15.1.1 全局对象的值属性
[...]
15.1.1.3 undefined
undefined 的值是未定义的 (参见8.1)。该属性有以下特性
{ [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

但是它仍然可以被本地变量覆盖:

(function() {
  var undefined = "foo";
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})()

或参数:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")

21
无法在 ES5 中重新定义。 - Ry-
7
在 ES5 中无法重新定义全局的 undefined 属性,但是可以使用本地变量来遮盖它。使用 void 0 更加简短且更安全。 - Oriol
1
我认为,如果你的代码正在重新定义未定义的内容,那么你需要处理的问题比是否使用typeof更大。 - chesscov77
是的,我同意,如果代码重新定义未定义的内容,问题会更大。但这与最佳实践的概念无关。 - Adrian Bartholomew

6

因为undefined并非总是被声明,但jQuery在其主函数中声明了undefined。所以他们在内部使用安全的undefined值,但在外部,他们使用typeof方式来确保安全。


6

4

对于局部变量,检查localVar === undefined就可以了,因为它们必须在局部范围内定义过,否则将不被视为局部变量。

对于既不是局部变量也没有定义的变量,检查someVar === undefined会抛出异常:未捕获的引用错误:j未定义

以下是一些代码,将澄清我上面所说的内容。 请注意内联注释以获得进一步的准确性

function f (x) {
    if (x === undefined) console.log('x is undefined [x === undefined].');
    else console.log('x is not undefined [x === undefined.]');

    if (typeof(x) === 'undefined') console.log('x is undefined [typeof(x) === \'undefined\'].');
    else console.log('x is not undefined [typeof(x) === \'undefined\'].');

    // This will throw exception because what the hell is j? It is nowhere to be found.
    try
    {
        if (j === undefined) console.log('j is undefined [j === undefined].');
        else console.log('j is not undefined [j === undefined].');
    }
    catch(e){console.log('Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.');}

    // However this will not throw exception
    if (typeof j === 'undefined') console.log('j is undefined (typeof(x) === \'undefined\'). We can use this check even though j is nowhere to be found in our source code and it will not throw.');
    else console.log('j is not undefined [typeof(x) === \'undefined\'].');
};

如果我们像这样调用上面的代码:
f();

输出将是这样的:
x is undefined [x === undefined].
x is undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

如果我们像这样调用上面的代码(实际上使用任何值):
f(null); 
f(1);

输出结果将会是:
x is not undefined [x === undefined].
x is not undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

如此检查:typeof x === 'undefined',实际上是在问这个问题:请检查变量x是否在源代码中存在(已被定义)。(多或少)。如果你了解C#或Java,这种类型的检查从未被执行,因为如果它不存在,它将不会编译。

<== 点击这里看演示效果 ==>


2

概述:

当在全局范围内,我们实际上希望如果变量未声明或其值为undefined时返回true:

var globalVar1;

// This variable is declared, but not defined and thus has the value undefined
console.log(globalVar1 === undefined);

// This variable is not declared and thus will throw a referenceError
console.log(globalVar2 === undefined);

在全局作用域中,我们不能确定一个变量是否被声明,这可能会导致引用错误。当我们对未知变量使用 typeof 运算符时,如果变量没有被声明,我们不会遇到这个问题:

var globalVar1;

console.log(typeof globalVar1 === 'undefined');
console.log(typeof globalVar2 === 'undefined');

这是因为typeof操作符在变量未声明或当前保持值undefined时返回字符串undefined,而这正是我们想要的。


  • 对于局部变量,我们不会遇到这个问题,因为我们预先知道该变量将存在。我们可以在相应的函数中简单地查找变量是否存在。
  • 对于对象属性,我们也不会遇到这个问题,因为当我们尝试查找不存在的对象属性时,我们也会得到值undefined

var obj = {};

console.log(obj.myProp === undefined);


-1

jQuery 可能希望您在函数中使用 letconst 变量,这在 JavaScript 的 ES6 2015 设计中不允许您在声明之前使用任何本地作用域(函数)letconst 变量。即使是 JavaScript 的变量提升也不能让您进行类型检查!

如果您尝试这样做,JavaScript 将生成错误,而与 var 变量不同,后者在变量提升时创建一个已声明但未初始化的变量,您可以对其进行类型检查或检查其是否未定义。

如果您在函数中声明了一个 letconst 变量,但在尝试访问它之后,typeof 检查仍然会在 JavaScript 中创建引用错误!这是一种非常奇怪的行为,对我来说毫无逻辑可言。但这就是为什么 jQuery 可能看不到使用 typeof 函数变量的用处。例如:

function MyError(){ 

    // WOW! This variable DOES NOT EVEN EXIST, but you can still check its type!
    if(typeof x === 'undefined')
    {
        alert(1);// OK!
    }

    // ERROR!
    // WOW! You cannot even check an existing "let" variable's TYPE in a local function!
    if(typeof y === 'undefined')//REFERENCE ERROR!!
    {
        alert(2);
    }
    // We defined the variable so its hoisted to the top but in a dead zone
    let y = 'test';
}

MyError();

// RESULT
// alert 1 fires but a REFERENCE ERROR is generated from the second alert 2 condition.

有趣的是,使用 typeof 检查不存在的本地变量是否为 'undefined' 是不可行的,但在函数中声明的 let 变量却可以!所以这可能就是我不依赖 jQuery 定义最佳实践的原因之一。 还存在一些特殊情况。

JavaScript 中关于 "undefined" 变量的更多怪异现象

undefined 有两种不同的表达式和三种不同的用法,如下:

  1. "typeof" 和 "undefined" 类型:未声明且不存在的变量没有被分配任何值,但其 "类型" 为 undefined。如果您访问甚至不存在的变量,即使测试原始默认值 undefined,也会生成 REFERENCE ERROR,直到分配一个值给已声明的变量。因此,在这种情况下检查 "typeof" 可以避免此错误:
    // In this first test, the variable "myVariable1" does not exist yet so creates
    // an error if we try and check if its assigned the default value of undefined!
    if (myVariable1 === undefined) alert(true);// REFERENCE ERROR!

    // Here we can elegantly catch the "undefined" type 
    // of the missing variable and stop the REFERENCE ERROR using "typeof".
    if (typeof myVariable1 === "undefined") alert(true);// true

    // Here we have declared the missing variable and notice its 
    // still an "undefined" type until initialized with a value.
    let myVariable1;
    if (typeof myVariable1 === "undefined") alert(true);// true

    // Lastly, after we assign a value, the type is no longer 
    // "undefined" so returns false.
    myVariable1 = 'hello';
    if (typeof myVariable1 === "undefined") alert(true);// false

JavaScript中访问但未声明的所有对象和类型都将默认为“undefined”类型。因此,这里的教训是首先尝试并检查typeof,以防止缺少变量错误!

  1. undefined原始值:在JavaScript中,所有已声明但尚未分配值的变量都被分配了原始undefined。如果您已经声明了一个变量,但尚未初始化它,那么它将被分配为默认的原始类型undefined。这与“undefined”类型不同。 undefined的原始值是一个保留值,但可以更改,但这不是所要求的。请注意,这仅捕获所有已声明但未初始化的变量:
    let myVariable3;
    if (myVariable3 === undefined) alert(true);// true

    let myVariable4 = 'hello';
    if (myVariable4 === undefined) alert(true);// false
  • 对象和undefined原始值:最后,JavaScript中的对象属性不像变量那样行为。对象属性在缺失时不会成为未定义类型,而是像未声明的变量一样,仅被分配了原始值undefined。因此它们的行为类似于#2:
  •     let myObject = {};
        if (myObject.myProperty === undefined) alert(true);// true
    

    最佳实践

    最后......这是一个非常好的理由,始终在您的所有JavaScript代码中检查变量的“undefined”类型和undefined原始值。大多数人会说,您很少需要两者。可能有一天,访问不存在的库中丢失的变量并创建nasty JavaScript REFERENCE ERROR! 因此,我总是按照此顺序进行检查,以停止JavaScript中的所有错误:

    if (typeof myVariable !== "undefined" && myVariable !== undefined) {
        // do something safe with myVariable!
    }
    

    -8

    typeof a === 'undefined' 在 node v6.9.1 上比 a === 'undefined' 快大约2倍。


    6
    你输入的不是同样的东西。我认为你在第二部分的意思是 undefined,而不是 'undefined' - scaryguy

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