【问题标题】:Cannot convert traditional for loop to for-each loop无法将传统的 for 循环转换为 for-each 循环
【发布时间】:2014-12-22 20:41:35
【问题描述】:

由于某些莫名其妙的原因,我无法将此循环转换为更自然的for (var player in fizzPlayers) { ... } 循环:

  for (var i = 0; i < fizzPlayers.length; i++) {
    var player = fizzPlayers[i];
    var val = parseInt(player.value);
    if (val != 0) {
      active.push(player);
      actfact.push(parseInt(player.value));
      actname.push(capitaliseFirstLetter(player.id));
    }
  }

我想用更自然的方式来写这个,像这样:

  for (var player in fizzPlayers) {
    var val = parseInt(player.value);
    if (val != 0) {
      active.push(player);
      actfact.push(parseInt(player.value));
      actname.push(capitaliseFirstLetter(player.id));
    }
  }

但它不起作用,你可以在底部的可运行代码sn-p中看到。

为了调试,我在循环之前插入了这段代码:

  // prints undefined... but why?
  for (var player in fizzPlayers) {
    console.log(player.value);
  }

它为 5 个玩家打印 undefined。为什么?程序前面有一个类似的循环,for (var player in fizzPlayers) { ... } 循环工作得很好。

为什么会这样?我错过了什么?

var fizzLoaded = false;
var fizzDiv, fizzFrom, fizzTo, fizzPlayers;

function fizzLoad() {
  if (fizzLoaded) {
    return;
  }
  fizzLoaded = true;
  var fizzForm = document.getElementById('fizzbuzz');
  fizzFrom = document.getElementById('rangeFrom');
  fizzTo = document.getElementById('rangeTo');
  fizzPlayers = [
    document.getElementById('frodo'),
    document.getElementById('sam'),
    document.getElementById('merry'),
    document.getElementById('pippin'),
    document.getElementById('bilbo')
  ];
  fizzDiv = document.getElementById('fizzOut');
}

function restrictRange() {
  var rFrom = parseInt(fizzFrom.value);
  var rTo = parseInt(fizzTo.value);
  fizzTo.min = rFrom;
  fizzFrom.max = rTo;
}

function validateValues() {
  var rFrom = parseInt(fizzFrom.value);
  var rTo = parseInt(fizzTo.value);
  if (rTo < rFrom) {
    alert("Illegal range from " + rFrom + " to " + rTo);
    return false;
  }
  for (var player in fizzPlayers) {
    var val = parseInt(player.value);
    if (val < 0 || val > 100) {
      alert("Illegal value " + val + " for player " + player.id);
      return false;
    }
  }
  return true;
}

function capitaliseFirstLetter(string) {
      return string.charAt(0).toUpperCase() + string.slice(1);
}

function fizzing() {
  fizzLoad();
  restrictRange();
  if (!validateValues()) {
    fizzDiv.innerHTML = "Illegal inputs";
    return;
  }

  var table = "";
  var rFrom = parseInt(fizzFrom.value);
  var rTo = parseInt(fizzTo.value);
  var active = [];
  var actfact = [];
  var actname = [];

  // prints undefined... but why?
  for (var player in fizzPlayers) {
    console.log(player.value);
  }

  for (var player in fizzPlayers) {
    var val = parseInt(player.value);
    if (val != 0) {
      active.push(player);
      actfact.push(parseInt(player.value));
      actname.push(capitaliseFirstLetter(player.id));
    }
  }

  table += "<table>\n";
  table += "  <tr><th>Value</th><th>Message</th></tr>\n";
  for (var i = rFrom; i <= rTo; i++) {

    var msg = "";
    for (var p = 0; p < active.length; p++) {
      if (i % actfact[p] == 0) {
        msg += actname[p];
      }
    }
    if (msg == "") {
      msg = "" + i;
    }
    table += "  <tr><td>" + i + "</td><td>" + msg + "</td></tr>\n";
  }
  table += "</table>\n";

  fizzDiv.innerHTML = table;
}
h1 {
    clear: left;
}

hr {
    clear: left;
}

label {
    display: inline-block;
    float: left;
    clear: left;
    width: 150px;
    text-align: left;
}
input {
  display: inline-block;
  float: right;
  text-align: right;
  padding-left:10px;
  width: 50px;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>FizzBuzz</title>
    <link rel="stylesheet" href="fizzbuzz.css">
    <script src="fizzbuzz.js" type="text/javascript"></script>
  </head>
  <body>
    <h1>Config</h1>
    <form id="fizzbuzz">
      <fieldset id="fizzControl" oninput="fizzing();">
        <label>Range From<input id="rangeFrom" type="number" min="1" max="100" value="1" required></label>
        <label>Range To<input id="rangeTo"   type="number" min="1" max="1024" value="100" required></label>
        <div id="players" >
          <label>Frodo<input id="frodo" type="number" min="0" max="100" value="3" required></label>
          <label>Sam<input id="sam" type="number" min="0" max="100" value="5" required></label>
          <label>Merry<input id="merry" type="number" min="0" max="100" value="0" required></label>
          <label>Pippin<input id="pippin" type="number" min="0" max="100" value="0" required></label>
          <label>Bilbo<input id="bilbo" type="number" min="0" max="100" value="0" required></label>
        </div>
      </fieldset>
    </form>
    <hr>
    <h1>Output</h1>
    <div id="fizzOut" >Change a value to get output (a snippet thing)</div>
    <script>fizzing();</script>
  </body>
</html>

【问题讨论】:

  • 对,for..in 循环与常规 for 循环不同。你不应该使用for..in 来循环数组。

标签: javascript for-loop


【解决方案1】:

编写循环遍历数组元素的惯用方法是forEach。正如 cmets 所指出的,for... in 用于对象键,而不是数组元素。

fizzPlayers.forEach(function(player) {
  var val = parseInt(player.value);
  if (val != 0) {
    active.push(player);
    actfact.push(parseInt(player.value));
    actname.push(capitaliseFirstLetter(player.id));
  }
});

您可以在all modern browsers (IE9 +)Array.prototype.forEach

MDN docs

【讨论】:

  • 谢谢,这非常有用!你有关于这个主题的正确文档的链接吗?
  • MDN 是 JavaScript 文档的好来源:developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
  • 谢谢,请将其编辑到您的答案中,这样每个人都会更容易阅读。顺便说一句,为什么for...in 循环工作似乎在validateValues 函数中运行良好?
  • player 指的是数组中每个元素的索引,比如0。数字没有value 属性,所以player.valueundefinedparseInt(undefined)NaN,既不小于0也不大于100。
【解决方案2】:

检查实际分配给player的内容:

> fizzPlayers = ["jim", "bob", "joe"]
> for (var player in fizzPlayers) {
    console.log(player);
  }
0
1
2

遍历一个对象会给你。数组的键是索引。在这种情况下,最好使用常规的 for 循环而不是 for-each 循环,因为如果您为数组分配了任何其他属性(如 fizzPlayers.barg = 40;),for-each 循环也会为您提供这些属性名称


但是,如果你使用的是 jQuery,你可以使用$.each:

> $.each(fizzPlayers, function (index, player) { 
    console.log(index + ": " + player); 
  });
0: jim
1: bob
2: joe

注意回调有两个参数:索引和值。

【讨论】:

    【解决方案3】:

    在 Javascript 中,for-each 循环将 index 分配给控制变量,因此您需要使用 fizzPlayers[player] 而不仅仅是 player。你可能不喜欢它(我经常不喜欢),但这就是它的工作原理。

    【讨论】:

      【解决方案4】:

      你需要

      for (var p in fizzPlayers) {
          console.log(fizzPlayers[p].value);
      }
      

      这是一个常见的烦恼。 ECMAScript 6 proposal

      for (var player of fizzPlayers) {
          console.log(player.value);
      }
      

      【讨论】:

        【解决方案5】:

        JavaScript 中的 for in 循环遍历对象的可枚举属性。因此,您不能将其转换为经典的 for 循环。更正式的,来自MDN

        for...in 循环仅迭代可枚举的属性。对象 从 Array 和 Object 等内置构造函数创建 从 Object.prototype 继承的不可枚举属性和 String.prototype,例如 String 的 indexOf() 方法或 Object 的 toString() 方法。循环将遍历所有可枚举 对象本身的属性以及对象从其继承的属性 构造函数的原型(更接近对象的属性) 原型链覆盖原型的属性)。

        【讨论】:

          【解决方案6】:

          for-each 循环分配索引

          // prints undefined... but why?
          for (var playerIndex in fizzPlayers) {
             console.log(fizzPlayers[playerIndex].value);
          }
          

          您可以考虑使用像 lo-dash 这样的便利库来实现实用功能。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-09-03
            • 1970-01-01
            • 2015-11-12
            • 1970-01-01
            • 2014-03-15
            • 1970-01-01
            相关资源
            最近更新 更多