在 JavaScript 中假设 undefined 没有被覆盖到底有多危险?

Posted

技术标签:

【中文标题】在 JavaScript 中假设 undefined 没有被覆盖到底有多危险?【英文标题】:How dangerous is it in JavaScript, really, to assume undefined is not overwritten? 【发布时间】:2012-02-05 16:45:09 【问题描述】:

每当有人提到针对undefined、it's pointed out 进行测试时,undefined 不是关键字所以it could be set to "hello",所以you should use typeof x == "undefined" 代替。这对我来说似乎很荒谬。没有人会这样做,如果他们这样做了,就足以有理由永远不要使用他们编写的任何代码......对吗?

我发现有人不小心将undefined 设置为null 的one example,这是避免假设undefined 未被覆盖的原因。但如果他们这样做了,这个 bug 就不会被发现,而且我看不出有什么更好的地方。

在 C++ 中,每个人都清楚使用#define true false 是合法的,但没有人建议您避免使用true 而使用0 == 0。你只是假设没有人会像个大混蛋那样做,如果他们这样做了,就再也不要相信他们的代码了。

这是否真的咬过某人,而其他人分配给undefined(故意)并且它破坏了您的代码,或者这更像是一种假设的威胁?我愿意抓住机会让我的代码更具可读性。这真的是个坏主意吗?

重申一下,我不是询问如何防止重新分配未定义。这些技巧我已经看过 100 遍了。我在问不使用这些技巧有多危险。

【问题讨论】:

我很乐意,但我觉得给出的三个答案中的任何一个都不能很好地回答这个问题。我试图表明我并没有要求我重新链接到的答案,但这仍然是我得到的。如果即使这不是我所要求的,也认为不接受答案被认为是愚蠢的,请告诉我,我会继续接受! 我认为所有的事实都在这里列出。所以,你说的是你想要一个主观的答案或意见,这只会导致分歧。这就是问题标题中不允许使用“最佳实践”这一短语的原因。你是唯一知道在你的场景中有多危险的人。如果你正在编写一个流行的库,你不能控制所有的“变量”,那么 function(undefined) 包装器似乎是一个很好的答案。为什么不使用它并接受这个答案? 如果有人担心有人重新定义undefined,你应该更担心有人重新定义XMLHttpRequest,或alert。我们在window 中使用的所有函数都可以重新定义。而且,如果您只是担心同事不小心这样做,为什么要相信他们不会这样做window.addEventListener = "coolbeans"?答案是不用担心这些。如果有人恶意将 JS 注入到您的页面中,那么您无论如何都会被淹没。首先努力防止那种的发生。 【参考方案1】:

不,我从来没有。这主要是因为我在现代浏览器上进行开发,这些浏览器大多兼容 ECMAScript 5。 ES5 标准规定 undefined 现在是只读的。如果你使用严格模式(你应该),如果你不小心尝试修改它会抛出一个错误。

undefined = 5;
alert(undefined); // still undefined
'use strict';
undefined = 5; // throws TypeError

你应该做的是创建你自己的作用域,可变undefined

(function (undefined) 
    // don't do this, because now `undefined` can be changed
    undefined = 5;
)();

常数很好。仍然没有必要,但很好。

(function () 
    const undefined = void 0;
)();

【讨论】:

您的第一条语句仅确保您在开发时不会意外覆盖 undefined,在客户端计算机上与其他代码一起运行时仍然会造成同样的威胁。 @bennedich:我知道,我是说我从来没有遇到过这个问题,但是这是你应该做的 @bennedich 你不会有同样的机会意外覆盖任何其他变量吗?【参考方案2】:

在为支持 ECMAScript 5.1 的浏览器编码时,您可以在代码中使用 undefined,因为它是 immutable according to the language specification。

另请参阅this 兼容性表或this 可以使用 ECMAScript 5 以了解所有现代浏览器 (IE 9+) 都实现了不可变 undefined

【讨论】:

【参考方案3】:

首先,如果你的代码出现问题,那可能不是因为其他开发人员如你所说的那样“试图成为一个混蛋”。

undefined 确实不是关键字。但它一个全局级别的原语。它原本打算这样使用(请参阅developer.mozilla.org 的“未定义”):

var x;
if (x === undefined) 
    // these statements execute

else 
    // these statements do not execute

常见的替代方法(也来自MDN),我认为更好的方法是:

// x has not been declared before
if (typeof x === 'undefined')  // evaluates to true without errors
    // these statements execute


if(x === undefined) // throws a ReferenceError


这有几个优点,显而易见的一个(来自 cmets)是它不会在未声明 x 时触发异常。还值得注意的是,MDN 还指出,在第一种情况下使用 === 而不是 == 很重要,因为:

var x=null;
if (x === undefined) 
    // this is probably what you meant to do
    // these lines will not execute in this case

else if (x == undefined) 
    // these statements will execute even though x *is* defined (as null)

else 
    // these statements do not execute

这是另一个经常被忽视的原因,为什么在所有情况下只使用第二种选择可能更好。

结论:第一种方式编码没有错,当然也不危险。您所看到的作为反对它的示例的论点(它可以被覆盖)并不是用typeof 编码替代方案的最有力论据。但是使用 typeof 更强大的原因有一个:当你的 var 没有被声明时,它不会抛出异常。也可以说使用== 而不是=== 是一个常见的错误,在这种情况下,它并没有达到你的预期。那么为什么不使用typeof呢?

【讨论】:

“这有几个优点,最明显的一个是在未声明 x 时不会触发异常。”让我说清楚。您认为静默忽略使用未声明变量时发生的异常是一种优势吗?这似乎很容易出错。你能解释一下为什么你认为这不是一个可怕的劣势吗?【参考方案4】:

一点也不危险。它只能在 ES3 引擎上运行时被覆盖,并且不太可能再使用。

【讨论】:

【参考方案5】:

针对未定义进行测试是安全的。正如你已经提到的。如果你得到一些覆盖它的代码(这是高度可改进的),就不要再使用它了。

也许如果您正在创建一个供公众使用的库,您可以使用一些技术来避免用户更改它。但即使在这种情况下,也是他们的问题,而不是您的图书馆。

【讨论】:

【参考方案6】:

只有您知道您使用什么代码,因此知道它有多危险。此问题无法以您已澄清您希望它回答的方式来回答。

1) 创建一个团队策略,不允许重新定义 undefined,保留它以供更流行的使用。扫描现有代码以获取未定义的左分配。

2)如果您不能控制所有场景,如果您的代码在您或您的策略控制的情况之外使用,那么显然您的答案是不同的。扫描确实使用您的脚本的代码。哎呀,如果您愿意,可以扫描网络以获取未定义的左分配的统计信息,但我怀疑这已经为您完成了,因为在这里只追求答案 #1 或 #3 会更容易。

3) 如果那个答案不够好,那可能是因为你需要一个不同的答案。也许您正在编写一个将在公司防火墙内使用的流行库,而您无权访问调用代码。然后在这里使用其他好的答案之一。注意流行的 jQuery 库实践声音封装,并开始:

(function( window, undefined ) 

只有您才能以您寻求的特定方式回答您的问题。还有什么要说的?

编辑:附注如果你真的想要我的意见,我会告诉你这根本不危险。任何很可能导致缺陷的东西(例如分配给未定义,这显然是一个有据可查的风险行为)本身就是一个缺陷。缺陷就是风险。但这只是在我的情况下,我有能力持有这种观点。正如我建议的那样,我为我的用例回答了这个问题。

【讨论】:

【参考方案7】:

对此有一个简单的解决方案:与始终未定义的void 0 进行比较。

请注意,您应该避免使用==,因为它可能会强制这些值。请改用===(和!==)。

也就是说,如果有人在将某物与undefined 进行比较时写了= 而不是==,则可能会错误设置未定义的变量。

【讨论】:

IMO,这是最好的答案。 请考虑foo === void 0 可能不像foo === undefined 那样流畅,并且现代浏览器(IE 9+)完全支持不可变undefined,正如您在this 兼容性表中看到的那样。 【参考方案8】:

没有合适的代码会做这样的事情。但是你永远无法知道你正在使用的一些想成为聪明的开发人员或插件/库/脚本做了什么。另一方面,这是极不可能的,现代浏览器根本不允许覆盖undefined,因此如果您使用这样的浏览器进行开发,您会很快注意到是否有任何代码试图覆盖它。


即使您没有提出要求 - 许多人在寻找更常见的“如何防止重新定义 undefined”问题时可能会发现这个问题,所以我还是会回答这个问题:

有一个很好的方法可以得到一个真正的undefined undefined,不管浏览器有多旧:

(function(undefined) 
    // your code where undefined is undefined
)();

之所以有效,是因为未指定的参数始终为undefined。你也可以用一个接受一些真实参数的函数来做到这一点,例如当你使用 jQuery 时就像这样。以这种方式确保一个健全的环境通常是一个好主意:

(function($, window, undefined) 
    // your code where undefined is undefined
)(jQuery, this);

那么您可以确定在该匿名函数中以下内容是正确的:

$ === jQuery window === [the global object] undefined === [undefined]

但是,请注意,有时typeof x === 'undefined' 实际上是必要的:如果变量x 从未设置为值(与设置undefined 相反),则读取x以不同的方式,例如if(x === undefined) 将引发错误。不过这不适用于对象属性,因此如果您知道 y 始终是一个对象,那么 if(y.x === undefined) 是绝对安全的。

【讨论】:

您关于x 未设置为值的说法并不完全正确(请参阅jsfiddle.net/MgADz);而是如果它真的是未定义(参见jsfiddle.net/MgADz/1)。 再一次,如果有人不小心说undefined = someVariable,那是一个错误,而您想要破坏。至少我会。 我使用了一个代码库,该代码库有一个缩小的遗留脚本覆盖了 undefined,我们没有时间或预算来重写脚本,这是 typeof x == "undefined" 的示例需要,这并不奇怪,可能是一个好习惯。 我对为什么所有示例都使用“未定义”作为函数中的附加参数感兴趣?但是,如果有人错误地传递了一个额外的参数(定义)并且所有逻辑都将失败,则可能会出现这种情况。为什么不使用类似 function() var _undef; if( someVal == _undef) 做某事 ; @Oleksandr_DJ 没错。组成一个您知道undefined 的值,并将其分配给范围内的undefined如果您传入“太多”参数,请不要让undefined 处于打开状态以供覆盖。 这会导致错误,并且是一种固有的非防御性模式,尤其是当Lucero points out 存在在范围内获得可靠的undefined 值的非常简单的替代方法。

以上是关于在 JavaScript 中假设 undefined 没有被覆盖到底有多危险?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript推断undefined的技巧

void 0 或者 undefined

JavaScript中void 0和undefined

在 Javascript 中检查 undefined'ness 时,我应该使用 != 还是 !==?

JavaScript中null和undefined的区别

javascript 中not defined 和undefined有什么区别