如何正确检查全局变量是否存在?

80

JSLint 不将此代码视为有效代码:

/* global someVar: false */
if (typeof someVar === "undefined") {
    var someVar = "hi!";
}

什么是正确的方式?


1
请注意,var不会作用于if块内部。这就好像在if之前写了var someVar一样。参考资料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/var#var_hoisting - leewz
3
在这里只阅读答案中的代码而不是问题中的代码后,我陷入了一个巨大的兔子洞。如果您想编写适用于Nodejs和浏览器的代码,那么无论JSLint对此有何看法,这都是检查变量是否未定义的正确方法。 - Tobias Cohen
11个回答

100
/*global window */

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

if (!window.hasOwnProperty('someVar')) {
    window.someVar = 123456;
}

6
您可以使用/*jslint browser: true */代替/*global window */ - XP1
13
为了得到相同的结果,第二种解决方案应该是 if(!window.hasOwnProperty('someVar')) - aaaaaa
2
如果页面在您的脚本之前运行以下脚本:undefined = 'some value',会怎样? - gion_13
3
请注意,"window"本身在Web Worker中未定义。为了最大兼容性,请先检查窗口的存在。 - Offirmo
7
对此你有何想法吗?在这种情况下,"window"是一个全局变量,我们想要检查其是否存在 - 当然,这也是我们想要解决的问题。 - James_pic
显示剩余8条评论

16
/**
 * @param {string} nameOfVariable
 */
function globalExists(nameOfVariable) {
    return nameOfVariable in window
}

无论您是使用 var foo 还是 window.foo 创建全局变量,都不会影响结果——在全局上下文中创建的变量会被写入 window 对象中。


5
当然,您不必使用一个函数,您可以在任何地方直接使用"foo" in window - gvlasov
3
Hrmmm,JSlint提示:“Unexpected 'in'。与undefined进行比较,或者使用hasOwnProperty方法。”那么,我猜正确的方式是:window.hasOwnProperty("nameOfVar") - gvlasov
2
@Susei:jsLint是不正确的。.hasOwnProperty()是有问题的,因为可能会有一个全局变量在window的原型链中更深处。它仍然是一个全局变量,但是.hasOwnProperty()无法识别它。使用in或者undefined测试才是正确的方法。 - user2437417
1
在NodeJS终端中无法运行此代码;因为window在那里是未定义的。 - cst1992
在 React 应用程序中,如果您想在 JSX 中进行内联检查,则以下是一个好的答案。 - Felipe

14
如果你想要仅在全局变量不存在时分配它,可以尝试以下代码:

如果您想要分配一个全局变量, 只有当它不存在时,请尝试:

window.someVar = window.someVar || 'hi';
或者
window['someVar'] = window['someVar'] || 'hi';

这是最优雅和最干净的解决方案。 - Christian
1
如果全局变量为假,这将无法按预期工作。如果 someVar 包含像 0'' 这样的值,它将把 'hi' 分配给它,而不是使用现有的值。 - hungerstar

9

尝试

variableName in window

或者

typeof window[variableName] != 'undefined'

或者

window[variableName] !== undefined

或者

window.hasOwnProperty(variableName)

2
你是否尝试在JSLint上测试这些代码? "'someVar' in window","typeof window['someVar'] !== 'undefined'","window['someVar'] !== undefined"(不将其转换为"window.someVar !== undefined")在JSLint上是不好的。无论如何,感谢你的好答案 :) - Ebrahim Byagowi
1
"object" 中的 "prop" 是完全合法的 JavaScript。请记住,JSLint 是为您服务的,而不是您为 JSLint 服务的,JSLint 不是 JavaScript。对我来说,if ('prop' in obj) { do_it(); } 更清晰(即可读性更强且易于维护),比 if (obj.prop !== undefined) { do_it(); } 更好。我建议您在这种情况下忽略 JSLint。另请参阅 https://dev59.com/7mw15IYBdhLWcg3wGn9c。 - kstep
你说得没错,但我宁愿保持我的代码经过 JSLint 验证,因为这样更容易维护。 - Ebrahim Byagowi

8

我认为这实际上是一个JSLint的问题。它会发出以下错误:

意外的 'typeof'。直接与 'undefined' 比较。

我认为这是错误的建议。在JavaScript中,undefined是一个全局变量,通常是未定义的。但是一些浏览器允许脚本对其进行修改,例如: window.undefined = 'defined'。如果是这种情况,直接与undefined进行比较可能导致意外的结果。幸运的是,当前符合ECMA 5标准的浏览器不允许对undefined进行赋值(在严格模式下会抛出异常)。

我喜欢你发布的typeof someVar === "undefined" ,或者像Susei建议的someVar in window


3
今天特别重要的是,并非所有的JS都能在浏览器中运行。如果你正在编写一个库,你希望你的代码能够同时适用于浏览器和像Node.js这样的运行时环境,在这些环境中,window对象并不存在。 - Chris Wininger

6
if (typeof someVar === "undefined") {
    var someVar = "hi!";
}

这段代码会检查 someVar(本地或全局)是否未定义。

如果想要检查全局变量,可以使用

if(window['someVar'] === undefined) {
    ...
}

假设这是在浏览器中 :)

6
自ES6以来,包括被接受的答案在内的大多数其他答案都是错误的,因为由let或const定义的全局变量,或由class声明生成的全局变量,在全局对象(浏览器中的window或node.js中的global)上没有相应的属性。其中几个 - 主要是使用typeof的那些 - 也可能会被存在但设置为undefined的全局变量所欺骗。
唯一完全通用的测试全局变量是否存在的方法 - 不管它是使用var、let还是const声明的,还是通过函数或类声明创建的,或者是通过在程序顶层创建myVar = value(而没有对myVar进行任何声明)或通过在全局对象上创建属性(即window.myVar = value)创建的 - 是尝试通过全局eval访问它,并查看是否抛出TypeError。

这是基于Ferran Maylinch提出的一个想法构建的(链接),但通过一种技巧来确保即使封装在函数中,它也能正常工作。

function globalExists(varName) {
  // Calling eval by another name causes evalled code to run in a
  // subscope of the global scope, rather than the local scope.
  const globalEval = eval;
  try {
    globalEval(varName);
    return true;
  } catch (e) {
    return false;
  }
}

undeclared = undefined;
const myConst = undefined;
let myLet;
var myVar;

globalExists('undeclared')    // => true
globalExists('myConst')       // => true
globalExists('myLet')         // => true
globalExists('myVar')         // => true
globalExists('nonexistent')   // => false
globalExists('globalExists')  // => true - can see itself.
globalExists('varName')       // => false - not fooled by own parameters.
globalExists('globalEval')    // => false - not fooled by local variable.

请注意,这里使用了eval,因此所有常见的警告都适用:不应将不受信任的值作为参数提供,并且如果必须使用不受信任的值,则应检查varName是否是有效的JavaScript标识符。 对于此问题,这样做超出了范围,但可以使用(相当复杂的)正则表达式来完成 - 只需注意正确的正则表达式取决于您正在使用的ECMAScript版本,代码是否为脚本或(ES6)模块,它是否在异步函数中等等。

很有趣,你对logconst指针的发现很好。我对eval也感到好奇,如果用户没有提供varName,那么评估变量名不应该是危险的用法,是吗?它如何被武器化?无论如何,我在MDN: never_use_eval!中发现了这个有趣的部分,可以用Function(`"use strict";return ${varName}`)();来替换eval。我已经测试过了,它完全正常! - Gruber
@Gruber:globalExists 具有与 eval 相同的安全属性:只要其参数永远不受恶意操作者控制,它就是完全安全的。例如,如果您只传递有效变量名称的字符串文字,则它是无害的。我认为 永远不要使用 eval 不是 MDN 文档的亮点:Function 构造函数并不比 eval 更安全,我对所声称的速度优势持怀疑态度,并且 globalEval 技巧在范围控制方面同样出色。 - cpcallen

1

bfavaretto 错误了。

将全局 undefined 设置为一个值不会改变对未定义对象的测试。请在您最喜欢的浏览器 JavaScript 控制台中尝试:

var udef; var idef = 42;
alert(udef === undefined); // Alerts "true".
alert(idef === undefined); // Alerts "false".
window.undefined = 'defined';
alert(udef === undefined); // Alerts "true".
alert(idef === undefined); // Alerts "false".

这是因为JavaScript忽略了对未定义变量尝试设置的所有值。

window.undefined = 'defined';
alert(window.undefined); // Alerts "undefined".

4
你不应该仅仅为了反驳别人的答案而使用“回答”部分。如果你认为别人的答案有问题,要么在评论中指出你预见到的问题,以便他们进行修正,要么提供自己的有效解决方案来回答问题。 - Scott
1
这是相关的,但它并没有回答问题。我意识到这篇评论太长了,也许你可以在表达不同意见的同时回答下面的问题? - Tim Post
1
你本可以在我的回答下留言让我知道你的不同意见,我只是偶然看到了你的回答。关于我们“最喜欢的浏览器”,你说得有点对,问题是旧版浏览器没有防止这种情况。我会编辑我的回答以使其更加清晰明了。 - bfavaretto
不幸的是,尽管现代浏览器有限制,undefined 仍然可以被修改:这个函数是完全合法的:function foo(undefined, x) { if (x === undefined) { alert('undefined!'); } }。如果你调用 foo(3),它将 不会 弹出警告。教训是 'undefined' 不总是指全局属性的引用。 - Ted Hopp

1
这是执行检查的简单方法。
但是,如果变量名被声明并赋值为布尔值:false,则此检查将失败。
if(window.variableName){

}

如果 variableName 有任何 falsy 值(例如空字符串),则此方法无效,因此这可能不是一个足够的解决方案。 - Hinrich

1
我认为最好的解决方案是以下内容:
if(window.hasOwnProperty('foo')) {
    console.log('Variable is not declared');
}

以下解决方案将不起作用,如果变量被声明但没有被赋值(var foo;)。
typeof foo === 'undefined'

我曾经见过代码在变量未定义时在typeof处抛出错误,但现在typeof似乎在任何地方都可以工作。无论是从节点进行交互式操作(node -e "console.log(typeof foo === 'undefined')"),还是从devtools控制台(console.log(typeof foo === 'undefined') // true)和从文件中(node test.js // true)。即使使用了"use strict";,也能正常工作。 - Shanimal

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