1

根据这篇文章

http://www.mediaevent.de/javascript/globale-lokale-variablen.html

全局变量在 JS 中非常危险。

很抱歉它是德语的,但我要指出这篇文章的两个主要陈述。

第一个已经在头部声明的第二段中。

它说“在 JS 中全局变量是危险的,因为它们可以通过名称被其他脚本访问”到目前为止这很好,因为这主要是我想使用全局变量的方式,不是吗?

但在文章中,这听起来可能是随机发生的。这肯定不是预期的行为,是吗?

但更让我害怕的是倒数第二句话。它预测如果多次调用声明全局变量的函数,将产生内存泄漏。但是,如果名称仍然相同,怎么会发生这种情况呢?怎么会有多个同名的全局变量声明?还是这篇文章可能只是某个“半知半解”的人写的?或者只是写给一个根本不习惯全局和本地区别的人?还是 JS 真的是这样表现的?

现在举一个具体的例子:

我希望有人登录到我的页面来创建一个随机生成的令牌并通过单击登录提交它。在每个其他按钮上,我希望这个令牌由不同的函数访问并提交它,这样就可以重新生成新的登录密钥。

对于那个键,我正在考虑使用一个全局变量,它由一个函数声明并由另一个函数返回。但是由于我可能不止一次地生成/重新生成密钥,这会产生内存泄漏吗?还是我指的这篇文章可能只是戏剧化?如果这确实是 JS 的行为方式,那么在我的情况下,使变量可从不同函数访问的好方法是什么?

4

3 回答 3

9

全局变量的问题不是内存,也不是性能。

全局变量的问题完全不同。问题是它们引入了全局状态并且脚本没有绑定到命名空间

让我们一一解决这些问题。

拥有全局状态

这是这里最大的问题。编码要求模块的依赖关系是明确的,并且代码片段之间的通信非常清晰

当您有全局变量时,代码的哪一部分使用该变量几乎不那么清楚,并且您无法确定代码的哪一部分需要它,哪些不需要。

假设我有一个Zoo项目,我有一个Bathe清洁动物的服务。我没有将它传递Bathe给每个需要它的动物,而是将它放在一个全局名称空间中,我只需调用Bathe(myAnimal).

现在我想重组我的动物园,我想知道哪些动物需要洗澡,因为我想优化它。除了浏览我的整个代码之外,我无法知道这一点。为了查看我的 Giraffe 是否需要洗澡,我必须阅读 Giraffe 类的整个代码。相反,如果我传递Bathe给 Giraffe 的构造函数而不是使用它或在 giraffe 内部创建它(一个称为依赖注入的概念),我可以通过阅读签名看到 Giraffe 需要洗澡。

现在情况会变得更糟,如果我有state怎么办?如果我实际上是在多个地方更改全局变量,那么跟踪将变得非常困难。在多于几行的代码库中,这意味着您的状态到处都在变化,并且没有明确的指示是谁在更改它。

这是您应该完全避免使用全局变量的主要原因

脚本未绑定到命名空间

如果我在一个页面上有两个脚本并且我的第一个脚本A在全局命名空间中声明了一个变量,那么第二个脚本可以访问该变量。这很有用,因为脚本可以以这种方式进行交互,但它非常有害,因为这意味着脚本可以覆盖彼此的代码,并以不明确的方式进行通信。

如果您使用像 browserify 或RequireJS这样的模块加载器,这当然可以完全缓解,这意味着您的整个脚本只公开两个全局变量 -require然后define脚本加载是通过加载器完成的。

这样,独立代码片段的交互方式就得到了很好的定义。这不会阻止您在全局对象上创建变量,但它有助于减少以统一方式这样做的需要。

关于安全的说明

当然,客户端的任何东西都会受到损害,你不能在不安全的浏览器上的客户端 JavaScript 中做安全或类似的事情(也就是说,你没有阻止任何外部的东西),因为客户端可以运行任意代码在您的代码上并阅读它。

于 2013-10-11T08:48:18.333 回答
6

全局变量存在三个大问题:

  1. 名称冲突
  2. 代码复杂度
  3. 垃圾收集

名称冲突

在全局范围内拥有变量的问题是您对该范围内的其他内容的控制较少。您的代码在全局范围内使用 ga_ 变量并且工作正常,但是当您添加使用相同变量的 Google Analytics 代码段时,事情会意外失败,并且很难理解为什么您的购物车在 3 个页面加载中有 2 个失败。

如果您可以将代码包装在 IIFE 中以防止在全局范围内包含任何变量,那么您应该这样做。显然,在某些情况下,您实际上希望您的代码可以全局访问(例如:jQuery 库)。在这些情况下,最好将所有内容保存在具有相关名称的单个命名空间 (jQuery) 中。

代码复杂度

对代码进行分区通常是一个好主意,以便各个部分之间的交互最少。交互的部分越多,进行更改和追踪错误的来源就越困难。显然,全局变量可以在任何地方访问,因此当您对访问全局变量的某些代码有问题时,您必须检查该变量的每次使用情况,这可能会非常痛苦。为了避免这些痛苦,要做的就是尽可能地保持变量是本地的,并封装代码片段,这样它们就不能相互交互,除非通过特定的接口。

内存泄漏

在 JavaScript 中,您几乎无法控制垃圾收集过程。所有可以保证的是,如果您可以访问一个变量,它将不会被垃圾收集。这意味着如果您希望对某些内容进行垃圾收集,那么您必须确保您无法再访问它。虽然i保持数字的全局变量不会有什么大不了的,正如@Boluc Papuaccoglu 提到的,当你的全局变量随着时间的推移保持越来越多的属性(例如 XHR 请求数组或创建的 DOM 对象数组)时,内存消耗变成一个大问题。

所有这些情况都是最坏的情况,您可能不会遇到小型应用程序的问题。当您开始学习编程时,这些建议最有价值,因为它们会养成良好的习惯,并且当您处理复杂的应用程序时,它们可以节省您在调试或难以进行改进上浪费的时间和金钱。

于 2013-10-11T08:59:02.887 回答
0

关于内存泄漏:假设您有一个函数,并在其中定义 a var,并将其用于某种目的,然后从该函数返回。在这种情况下,变量使用的内存将被释放。但是,如果您依靠全局变量来做同样的事情,那么在您的函数退出后很长时间内,内存将继续分配。扩展相同的场景,假设您的函数向该变量添加属性,其名称取决于函数正在处理的数据(如订单 ID、客户名称等)。现在,每次调用您的函数时,都会有越来越多的属性被附加到这个变量上,它会不断增长。

于 2013-10-11T08:14:13.947 回答