【问题标题】:Should I encapsulate blocks of functionality in anonymous JavaScript functions?我应该将功能块封装在匿名 JavaScript 函数中吗?
【发布时间】:2011-04-14 19:04:25
【问题描述】:

我的直觉是,将代码块封装在匿名函数中是个好主意:

(function() {
  var aVar;
  aVar.func = function() { alert('ronk'); };
  aVar.mem = 5;
})();

因为我不再需要aVar,所以我假设垃圾收集器会在aVar 超出范围时删除它。这是正确的吗?或者解释器是否足够聪明,可以看到我不再使用该变量并立即清理它?是否有任何理由(例如样式或可读性)我应该以这种方式使用匿名函数?

另外,如果我给函数命名,像这样:

var operations = function() {
  var aVar;
  aVar.func = function() { alert('ronk'); };
  aVar.mem = 5;
};
operations();

operations 是否一定会一直存在直到超出范围?或者解释器可以立即告诉它何时不再需要?

更好的例子

我还想澄清一下,我不一定要谈论全局范围。考虑一个看起来像

的块
(function() {

  var date = new Date(); // I want to keep this around indefinitely

  // And even thought date is private, it will be accessible via this HTML node
  // to other scripts.
  document.getElementById('someNode').date = date;

  // This function is private
  function someFunction() {
    var someFuncMember;
  }

  // I can still call this because I named it. someFunction remains available.
  // It has a someFuncMember that is instantiated whenever someFunction is
  // called, but then goes out of scope and is deleted. 
  someFunction();

  // This function is anonymous, and its members should go out of scope and be
  // deleted
  (function() {
    var member;
  })(); // member is immediately deleted
  // ...and the function is also deleted, right? Because I never assigned it to a
  // variable. So for performance, this is preferrable to the someFunction
  // example as long as I don't need to call the code again.

})();

我的假设和结论是否正确?每当我不打算重用一个块时,我不应该只将它封装在一个函数中,而是将它封装在一个匿名函数中,这样该函数就没有引用,并且在它被调用后被删除,对吧?

【问题讨论】:

  • 只是好奇,有没有考虑内存泄漏?

标签: javascript garbage-collection scope anonymous-function


【解决方案1】:

您添加到全局范围的任何内容都将保留在那里,直到页面被卸载(除非您专门将其删除)。

通常最好将属于同一范围的变量和函数放在本地范围或对象中,这样它们就可以尽可能少地添加到全局命名空间中。这样一来,重用代码就容易多了,因为您可以将不同的脚本组合在一个页面中,从而将命名冲突的风险降至最低。

【讨论】:

  • 对。我同意这一点。我的问题更多的是在匿名函数中粘贴单独的代码块以真正最小化它们的范围是否好。一个例子可能是我保留的一个对象,也许通过将它分配为 HTML 节点的成员,但其中的一个单独的块可以做它的事情然后被删除。我想我的问题更多是关于垃圾收集的细节,以及我应该在多大程度上影响我的风格。
【解决方案2】:

您说得对,将变量粘贴在匿名函数中是一种避免混淆全局对象的好习惯。

回答你的后两个问题:解释器完全不可能知道一个对象不会被再次使用,只要它有一个全局可见的引用。就解释器所知,您可以随时评估一些依赖于window['aVar']window['operation'] 的代码。

基本上,记住两件事:

  1. 只要有一个物体在附近,如果没有您的允许,它的任何插槽都不会被神奇地释放。
  2. 在全局上下文中声明的变量是全局对象的槽(window 在客户端 Javascript 中)。

综合起来,这意味着全局变量中的对象在脚本的整个生命周期内都存在(除非重新分配变量)。这就是我们声明匿名函数的原因——变量获得一个新的上下文对象,该对象在函数完成执行后立即消失。除了效率上的胜利,它还减少了名称冲突的机会。

不过,您的第二个示例(使用内部匿名函数)可能有点过分热心。我不会担心在那里“帮助垃圾收集器”——GC 可能无论如何都不会在该函数的中间运行。担心会持续存在的事情,而不仅仅是比其他情况稍长一些。这些自动执行的匿名函数基本上是自然属于一起的代码模块,因此一个很好的指导是考虑这是否描述了您正在做的事情。

不过,在匿名函数中使用匿名函数是有原因的。例如,在这种情况下:

(function () {
  var bfa = new Array(24 * 1024*1024);
  var calculation = calculationFor(bfa);
  $('.resultShowButton').click( function () {
    var text = "Result is " + eval(calculation);
    alert(text);
  } );
})();

这导致点击回调捕获了那个巨大的数组,因此它永远不会消失。您可以通过将数组隔离在其自己的函数中来避免这种情况。

【讨论】:

  • 我理解并同意将其用作避免混淆全局命名空间的一种措施。我认为我最初的例子没有充分展示我的问题的范围。我修改了我的问题以包含一个更好的例子。
  • 担心会持续存在的东西,而不仅仅是比其他情况稍长一些。这正是我想要的!感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-10
  • 2022-11-06
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
相关资源
最近更新 更多