根据这篇文章
http://www.mediaevent.de/javascript/globale-lokale-variablen.html
全局变量在 JS 中非常危险。
很抱歉,它是德语的,但我要指出这篇文章的 2 个主要陈述。
第一个已经在 head 语句的第 2 段中。
上面写着类似“在 JS 中,全局变量很危险,因为它们可以被其他脚本通过名称访问”之类的话,到目前为止还不错,因为这就是为什么我想使用全局变量的主要方式,不是吗?
但在文章中听起来这可能是随机发生的。这肯定不是预期的行为,是吗?
但更让我害怕的是倒数第二句。它预测如果一个函数声明一个
全局变量被多次调用。
但是,如果名称仍然相同,怎么会发生这种情况?怎么会有多个同名的全局变量?
或者这篇文章可能是某个“一知半解”的人写的?或者也许只是写给那些根本不习惯全局和本地之间差异的人?
还是 JS 真的以这种方式表现?
现在举个具体的例子:
我希望登录到我的页面的人创建一个随机生成的 token 并通过单击登录提交它。
在每个其他按钮上,我希望这个 token 由不同的函数访问并提交它,这样只有在新登录时才会重新生成 key 。
对于那个键,我正在考虑使用一个全局变量,它由一个函数声明并由另一个函数返回。
但是因为我可能会多次生成/重新生成 key ,这会产生内存泄漏吗?或者我指的这篇文章可能只是戏剧化?
如果这真的是 JS 的行为方式,那么在我的例子中,使变量可从不同函数访问的好方法是什么?
全局变量的问题不是内存,也不是性能。
全局变量的问题完全不同。问题在于它们引入了全局状态并且脚本未绑定(bind)到命名空间。
让我们一一解决这些问题。
拥有全局状态
这是这里的最大问题。编码要求模块的依赖关系是明确的并且代码片段之间的通信非常清晰。
当您拥有全局变量时,代码的哪一部分使用该变量几乎不那么清楚,您无法确定代码的哪一部分需要它,什么不需要。
假设我有一个 Zoo
项目,并且我有一个为动物清洁的 Bathe
服务。我没有将 Bathe
传递给每只需要它的动物,而是将它放在全局命名空间中,我只需调用 Bathe(myAnimal)
。
现在我想重组我的动物园,我想知道哪些动物需要洗澡,因为我想优化它。除了检查我的整个代码之外,我没有办法知道这一点。为了查看我的长颈鹿是否需要洗澡,我必须阅读长颈鹿类的整个代码。相反,如果我将 Bathe
传递给 Giraffe 的构造函数,而不是在 giraffe 内部使用或创建它(一个称为依赖注入(inject)的概念),我可以通过读取签名看到 Giraffe 需要洗澡。
现在情况可能会变得更糟,如果我有状态怎么办?如果我实际上在多个地方更改一个全局变量,它就会变得极难跟踪。在不止几行的代码库中,这意味着您的状态到处都在变化,但没有明确指示谁在改变它。
这是您应该完全避免使用全局变量的主要原因。
脚本未绑定(bind)到命名空间
如果我在一个页面上有两个脚本,并且我的第一个脚本在全局命名空间上声明了一个 A
变量,那么第二个脚本可以访问该变量。这很有用,因为脚本可以通过这种方式进行交互,但也非常有害,因为这意味着脚本可以覆盖彼此的代码,并以一种不明确的方式进行通信。
如果您使用像 browserify 或 RequireJS 这样的模块加载器,当然可以完全缓解这种情况。这意味着您的整个脚本仅公开两个全局变量 - require
和 define
,然后通过加载器完成脚本加载。
通过这种方式,独立代码片段的交互方式得到了很好的定义。这不会阻止您在全局对象上创建变量,但它有助于减轻以统一方式这样做的需要。
安全注意事项
当然,客户端的任何内容都会受到损害,您不能在不安全的浏览器上在客户端 JavaScript 中执行安全性或类似的操作(也就是说,您没有阻止任何外部操作),因为客户端只能在您的代码上运行任意代码并阅读它。