jQuery核心样式指南建议两种不同的方法来检查变量是否已定义。
- 全局变量:
typeof variable === "undefined"
- 局部变量:
variable === undefined
- 属性:
object.prop === undefined
为什么jQuery在全局变量和局部/属性变量中使用不同的方法?
jQuery核心样式指南建议两种不同的方法来检查变量是否已定义。
typeof variable === "undefined"
variable === undefined
object.prop === undefined
为什么jQuery在全局变量和局部/属性变量中使用不同的方法?
对于未声明的变量,typeof foo
会返回字符串字面量"undefined"
,而恒等式检查foo === undefined
会触发错误"foo未定义"。
对于本地变量(您知道在某处已经声明了),不会发生这样的错误,因此使用恒等式检查。
typeof foo; // -> "undefined"
中添加引号,以示强调它是一个字符串而不是原始值undefined
。 - c24w我建议你在任何地方都使用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
检查。
foo === undefined
可能会被缩写为 f===u
,而 typeof foo === "undefined"
只能被简化为 typeof f==="undefined"
。 - Patrick McElhaneytypeof
对未声明变量的安全性是否是一个优势。如果有任何问题,它更容易让拼写错误通过,而且我看不出你实际上想要检查未声明变量的类型的情况。 - David Tang使用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")
undefined
属性,但是可以使用本地变量来遮盖它。使用 void 0
更加简短且更安全。 - Oriol因为undefined
并非总是被声明,但jQuery在其主函数中声明了undefined
。所以他们在内部使用安全的undefined
值,但在外部,他们使用typeof
方式来确保安全。
variable === undefined
的性能提升感兴趣,可以参考这里,但似乎只是Chrome的优化。
对于局部变量,检查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,这种类型的检查从未被执行,因为如果它不存在,它将不会编译。
当在全局范围内,我们实际上希望如果变量未声明或其值为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);
jQuery 可能希望您在函数中使用 let
和 const
变量,这在 JavaScript 的 ES6 2015 设计中不允许您在声明之前使用任何本地作用域(函数)let
或 const
变量。即使是 JavaScript 的变量提升也不能让您进行类型检查!
如果您尝试这样做,JavaScript 将生成错误,而与 var
变量不同,后者在变量提升时创建一个已声明但未初始化的变量,您可以对其进行类型检查或检查其是否未定义。
如果您在函数中声明了一个 let
或 const
变量,但在尝试访问它之后,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 定义最佳实践的原因之一。 还存在一些特殊情况。
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
,以防止缺少变量错误!
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!
}
typeof a === 'undefined'
在 node v6.9.1 上比 a === 'undefined'
快大约2倍。
undefined
,而不是 'undefined'
。 - scaryguy
foo === undefined
是针对本地副本的undefined进行检查,而不是全局的(window.undefined),后者可能已被疯狂的代码修改过。undefined是可变的这一事实确实值得注意,我很高兴你提到了这一点。(+1) - Patrick McElhaney