【问题标题】:Javascript variable declaration within loop循环内的Javascript变量声明
【发布时间】:2016-01-22 17:16:03
【问题描述】:

我有一个近乎强迫的习惯,但我认为可能完全没有必要。使用如下代码:

function abc(){
  var a,b;
  for(var i=0;i<10;i++){
    a=document.getElementsByTagName('LI').item(i).width;
    b=document.getElementsByTagName('DIV').item(i).width;
    // now do something with a and b
   }
   return;
}

我强迫性地在循环之前声明变量,而不是:

function abc(){
  for(var i=0;i<10;i++){
   var a=document.getElementsByTagName('LI').item(i).width;
   var b=document.getElementsByTagName('DIV').item(i).width;
    // now do something with a and b
   }
   return;
}

请注意,在第二个代码块中,我在每次循环迭代时使用var 定义变量。我想第一个是可读性等方面的最佳实践。但有时我只是在破解一些东西,不需要遵循最佳实践。

我的问题是:

是否有任何理由定义将在循环内使用 var 关键字重新定义的变量?

【问题讨论】:

  • vars 无论如何都会被提升,包括for 语句中的那个。 IMO not 在函数顶部声明它们(包括你没有声明的函数)带有 暗示 不存在提升,只会让事情变得更难思考关于,尤其是当未来的读者可能无法完全理解 JS 时。这只是噪音。
  • 您需要使用 VAR 的唯一时间是当变量将被保存在外部(全局)时。在函数内部执行此操作意味着无论您是否对它进行 VAR,它都会在函数完成时下降。是否在循环中执行它并不重要。就是因为在函数里面没用。
  • @durbnpoisn。嗯 - 也许我误解了你,但这似乎与我的理解相反。如果我不使用 var 关键字在函数中定义变量,它将被拉入全局范围 - 从而产生与此问题无关的其他问题。
  • @DaveNewton。那么它纯粹是化妆品吗?假设,我在开发者控制台中进行黑客攻击并且不关心可读性,是否出于所有意图和目的,都一样?

标签: javascript performance loops variables


【解决方案1】:

由于 Javascript 中的变量提升,var 位于函数顶部或 for 循环内部之间的执行没有技术差异。如果这就是你所关心的,那么你可以做到这一点。

只是为了刷新内存,Javascript 提升意味着像你的第二个代码块这样的代码被解析然后像你的第一个代码块一样执行。函数内的所有var 声明在执行前都会自动移动到函数作用域的顶部。对这些变量的赋值保留在代码中它们所在的位置——只是移动了变量的声明。

因此,区别更多在于您希望编码的外观。当您将var 定义放在for 循环中时,它使代码看起来像是在为for 循环的每次迭代重新创建变量,即使情况并非如此。每次循环迭代都会为它们分配一个值,但不会创建一个新变量。如果您使用let 而不是var 就会出现这种情况,因为let 具有块作用域,而var 仅具有函数作用域。

一般来说,最好只将代码放入实际需要在循环中的循环中。虽然无论 var 是在循环内部还是外部,它实际上并不会改变执行中的任何内容,但这只是一种良好做法的一部分,而在循环内部或外部的其他代码可能会有所不同。

在你的情况下,我认为这是一个更好的做法:

function abc(){
  var liTags = document.getElementsByTagName('LI');
  var divTags = document.getElementsByTagName('DIV');
  var len = Math.min(liTags.length, divTags.length);
  var a,b;

  for(var i = 0; i < len; i++){
    a = liTags[i].width;
    b = divTags[i].width;

    // now do something with a and b

   }

   return;
}

在这里,您已经从循环中删除了对 document.getElementsByTagName() 的两个调用,这将产生巨大的性能差异。


2017 年更新。 Javascript 版本 ES6,现在支持 constlet 声明变量。它们是块范围的,而不是像var 这样的函数范围,因此如果您在for 循环块中声明其中一个,那么将为for 循环的每次调用创建一个新的单独变量。虽然这不会对您显示的代码类型产生任何显着的执行差异,但如果您在循环内有异步代码引用您声明的变量,它可能会产生差异。在循环体中使用constlet 的情况下,每个异步调用都会获得自己单独的变量副本,这有时会非常方便。

  for(var i = 0; i < len; i++){
      let a = liTags[i].width;
      let b = divTags[i].width;
      
      $.get(someUrl).then(function(data) {
          // each call to $.get() here in the loop has it's own a and b
          // variables to use here, which would not be the case with var
      });

   }

【讨论】:

  • 我认为,您回答的第一部分回答了我最初的问题。你说纯化妆品? (并不是说化妆品不重要,但在控制台中工作时并不重要)。但是你问题的第二部分对我提出了另一个问题。当liTags 抓取所有LI 元素时,是否会发生所有DOM 查找?如果是这样,迭代与迭代任何其他 array-like 对象没有什么不同?另请参阅我对@bultack 答案的评论。
  • @user1167442 - getElementsByTagName() 返回一个live HTMLCollection。这意味着它找到的所有元素最初都在那里,但是如果在您使用数据结构时 DOM 发生变化,结果将实时更新。这可能是一种祝福和一种诅咒(在我看来更常见的是一种诅咒),如果您在以会影响 HTMLCollection 本身的方式更改 DOM 的同时处理 HTMLCollection,则必须注意这一点。有时明智的做法是将 HTMLCollection 实际复制到静态数组中以防止其更改。
  • 谢谢。这对将来的参考非常有用。
【解决方案2】:

我知道这个答案对您的问题没有帮助,它只是一个建议,但我认为当您要访问多个变量时,将变量放入 DOM 元素是一个好习惯时间。这样做可以避免每次都遍历所有 DOM

function abc() {

  var a = document.getElementsByTagName('LI'),
      b = document.getElementsByTagName('DIV');

  for ( var i = 0; i < 10; i++ ) {

    a.item(i).width;
    b.item(i).width;
    // now do something with a and b

   }

   return;

}

【讨论】:

  • 您似乎错过了分配中的i,因此分配需要在for 循环内。
  • @jfriend00。你是对的。虽然我的代码是伪代码 - 他的回答会引发错误并且不起作用。
  • @jfriend00 现在已修复 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 1970-01-01
  • 2014-06-15
  • 2011-04-10
  • 1970-01-01
  • 2011-11-14
  • 1970-01-01
相关资源
最近更新 更多