JavaScript中如何正确使用“for...in”循环?

11

在我提出问题之前,我想让大家知道我很感激总有人愿意帮忙,并且我也会尽我所能回馈社区。谢谢。

现在,我想获取一些关于如何正确使用JavaScript中的“for...in”循环的指针,我已经做了一些研究并尝试了一些东西,但我仍然不清楚如何正确使用它。

假设在HTML表单中有一个随机数量的“select”标签,我不需要用户为所有标签选择选项,如果他们愿意,可以留下一些未触及的选项。但是我需要知道他们是否一个都没有选择,或者至少选择了一个。

我正在尝试使用“for...in”循环查找用户是否选择了任何标签。例如:

var allSelected = $("select option:selected");
var totalSelected = $("select option:selected").length;

第一个变量生成了所有选定选项的数组。 第二个变量告诉我在表单中有多少选定选项(选择标签可能不止一个,每次都会更改)。 现在,为了查看是否选择了任何选项,我循环遍历每个元素(选定的选项),并检索“value”属性。默认的“option”标记具有value =“0”,因此如果任何选定选项返回大于0的值,则我知道至少选择了一个选项,但是它不必按顺序,这是我的循环目前的情况:

for(var i = 0; i < totalSelected; i++){
  var eachOption = $(allSelected[i]).val();
  var defaultValue = 0;
  if(eachOption == defaultValue){
    ...redirect to another page
  }else if(eachOption > defaultValue){
    ... I display an alert
  }
}

我的问题在于一旦"if"匹配到一个值为0的元素,它就会立即将用户发送到下一页,而没有测试数组中其余的元素,而用户可能已经选择了第二或第三个选项。

我真正想做的是检查数组中的所有元素,然后采取下一步行动,我认为可以这样做,但我做不对:

var randomValue = 25;  
for(randomValue in allSelected){
  var found = true;
  var notFound = false
  if(found){
    display an alert
  }else{
    redirect to next page
  }
}

我使用的这个循环或逻辑是有缺陷的(我非常确定),我想做的是测试数组中的所有元素与一个变量进行比较,并根据下一步的操作进行相应的处理。

希望这对你们有些意义,任何帮助将不胜感激。
谢谢,
JC

5个回答

20

for..in(通常)

for..in 循环遍历对象的属性名称。人们认为它会遍历数组的索引,因为数组的索引实际上是数组对象的属性名称,但这是一个误解。

因此:

var obj = {foo: 1, bar: 2};

foobar是属性的名称,因此:

var name;
for (name in obj) {
    alert(name);
}

...将显示"foo"和"bar"(无特定顺序)。

我们稍后会回到数组。:-) 让我们先看看对象。

对象可以有自己的属性,以及从其原型对象继承的属性。上面的foobar属性是obj的直接属性,但是:

function Thing() {
}
Thing.prototype.foo = 1;

var t = new Thing();
t.bar = 2;

现在我们的 t 对象有 foobar,但是 foo 来自于原型,而 bar 是它自己的属性。在 for..in 循环中,我们可以检查它们分别属于哪个类型:
var name;
for (name in obj) {
    alert(name + "(" + (obj.hasOwnProperty(name) ? "own" : "inherited") + ")");
}

这将显示"foo(继承)"和"bar(自己的)"(没有特定的顺序)。

对象还可以具有非枚举属性 - 它们不会出现在 for..in 循环中。 这就是为什么如果你在数组上做一个 for..inlength 属性不会出现,因为 length 被定义为非枚举属性。几乎所有标准对象上的标准属性都是非可枚举的(包括指向函数的所有属性,例如 String 实例上的 toUpperCase 属性)。 (过去,只有规范中的那些可以是非枚举的,但 ECMAScript 5th 版现在为我们提供了方法来拥有自己的非枚举属性。)

所以,数组。 Javascript 中的数组与大多数其他语言中的数组完全不同。(它们不是连续的内存块。)Javascript 中的数组是具有一些特殊行为的标准对象:

  • length 属性始终设置为数组对象具有最高数字值的属性名称的下一个数字。 所以,如果您的最高数组索引是 2,则 length 将为 3。
  • 如果更改 length,任何属性的名称具有等于或高于您给出的数字的数值,将从对象中删除。

这些以及它们从 Array.prototype 继承的函数几乎是 Javascript 中数组的所有特殊功能。 它们的所有主要功能(存储和检索值)都只是标准的 Javascript 对象属性行为。

您的特定循环

由于上述原因,我不会为循环使用 for..in。 我要么使用 jQuery#each,要么使用像您所拥有的一样的老式循环。 后者看起来像这样:

var allSelected = $("select option:selected");
var totalSelected = allSelected.length; // <= No need to repeat the selector query
var index;
var anySelected = false;
for (index = 0; !anySelected && index < totalSelected; ++index) {
    if (allSelected[index].value !== "0") {
        anySelected = true;
    }
}
if (anySelected) {
    // do something with the selection
}
else [
    // do something about their not having picked one
}

好的,这很有道理。我不知道每次需要测试多少个选项(它会改变),但我知道可以选择哪些可能的值。 你认为我可以硬编码一个对象,然后检查所选值与该对象属性的匹配吗?谢谢。 - jnkrois
@jnrois:我已经更新了以解决你特定的循环问题。我有点沉迷于JavaScript方面的问题。 :-) - T.J. Crowder
1
这解释了为什么我的特定Javascript代码会得到混乱的结果。这很有用! - Wayne Werner
"Object.prototype.foo = 1;" 或许是一个更好的例子。 - Justin Johnson
@Justin:没有更好的例子,因为扩展Object.prototype是一个非常糟糕的想法。 :-) 这就是为什么我选择了Thing - T.J. Crowder
显示剩余5条评论

5
你需要知道JavaScript中的for-in语句是用来枚举对象属性的。
当你想要迭代类数组对象时,推荐始终使用顺序循环(forwhiledo...while)。
为什么你不应该使用for-in处理类数组对象:
  • 无法保证迭代的顺序
  • 还会枚举继承的属性
参见:

[ 1 ] 在此,我所指的“类数组”是指任何包含按顺序编号的属性和一个length属性的对象。


谢谢,我会再多阅读一些来学习它的基础知识。 - jnkrois
+1 这个想法太好了!主要区别在于迭代和枚举。我的唯一建议是更明确地说明另一类迭代,因为 for..in 并没有什么异常之处。将 forwhiledo..while 称为“顺序”或“计数”循环可能更合适。 - Justin Johnson
@Justin:同意,顺序循环 是更合适的术语。我认为问题在于当人们来自其他语言时,那些看起来相似的结构(例如 Java 或 C# 中的 foreach)会造成困惑... - Christian C. Salvadó

4

你的问题有两个方面。

  1. 只使用 "for ... in" 循环遍历对象的属性名称,永远不要用它来循环遍历数组(无论是真正的数组还是类似于数组的东西,比如 jQuery 对象)。

  2. 如果你正在使用 jQuery,那么这样做是反常规的:

    $("select option:selected").each(function() {
      // .. "this" 指向每个选项
    });

如果你想收集所有选择框的设置,可以遍历 所有<select> 标签,过滤出仅设置为其 "默认" 值的标签,然后将所有值收集到一个名称/值对数组中。


好的,谢谢,这很有道理,因为我需要每个元素的“value”属性的值,而“each()”真的不起作用,也不是“for in”,但现在我明白了原因。谢谢 - jnkrois

2

使用.each()方法来实现您想要达到的目标更加简单。

// this is to know if we want to redirect
var redir = true;
$("select option:selected").each(function() {
  var val = $(this).val();

  if (val > 0) {
    alert('Found one!');
  }      
  if (val != 0) { 
    redir = false;
    // you can return false here if you want to stop processing the each loop too!
  } 
});

if (redir) {
  window.location = "/nextpage";
}

请原谅我的无知,但是“redir”如何在“each”函数之外对我可用,是因为你在“each”开始之前声明了它吗? - jnkrois
@jnkrois - 是的,该变量在包含函数的作用域内,函数从其包含函数继承其基本变量。 - gnarf
谢谢,我以前因为这个问题卡住了。我会尝试这种方法。非常感谢。 - jnkrois
1
@jnkrois:关于在传递到“each”中的函数内部如何使用“redir”的问题:该函数是一个“闭包”,可以访问在定义它的范围内可用的任何内容。更多信息请参见:http://blog.niftysnippets.org/2008/02/closures-are-not-complicated.html - T.J. Crowder

1
我会这样做。
<html>
<head>
  <title>Test Page</title>
  <script src="http://code.jquery.com/jquery-latest.js"></script>
  <script type="text/javascript">

  $(function()
  {
    function checkSelected()
    {
      var DEFAULT = "0";
      var selected = [];

      $("select option:selected").each( function()
      {
        var $option = $(this);
        var optionValue = $option.val(); 
        if ( optionValue !== DEFAULT )
        {
          selected.push( {name: $option.parent().attr( 'name' ), value: optionValue } );
        }
      });

      if ( selected.length )
      {
        $.each( selected, function( index, item )
        {
          alert( item.name + ': ' + item.value );        
        });
      } else {
        alert( 'None selected!' );
      }
    }

    // just for this demo
    $('button').click( function( event )
    {
      event.preventDefault();
      checkSelected();
    })
  });


  </script>
</head>

<body>

<select name="one">
  <option value="0">Zero</option>
  <option value="1">One</option>
</select>

<select name="two">
  <option value="0">Zero</option>
  <option value="1">One</option>
</select>

<button>Test It</button>

</body>
</html>

我测试了一下,它完美地运行了。我会学习它,以便我能够理解你所做的和背后的逻辑。有时阅读代码比阅读真正的解释更容易,这很有趣。非常感谢你的回复。 - jnkrois
你应该注意到我使用了jQuery的each()方法进行循环,而不是原生的forfor..in循环。虽然这个例子当然也可以用标准的for循环完成。 - Peter Bailey
注意,我会尝试其中一种方法。 - jnkrois

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接