在JavaScript中使用for…of语法获取循环计数器/索引


316

警告:

问题仍然适用于for…of循环。>不要for…in用于遍历Array,而要用于遍历对象的属性。就是这样


我了解for…inJavaScript 的基本语法如下:

for (var obj in myArray) {
    // ...
}

但是,如何获取循环计数器/索引

我知道我可能可以做类似的事情:

var i = 0;
for (var obj in myArray) {
    alert(i)
    i++
}

甚至是好旧的:

for (var i = 0; i < myArray.length; i++) {
    var obj = myArray[i]
    alert(i)
}

但是我宁愿使用更简单的for-in循环。我认为它们看起来更好并且更有意义。

有没有更简单或更优雅的方式?


在Python中很简单:

for i, obj in enumerate(myArray):
    print i

6
不要用于数组。而且无论如何,它都会遍历属性名称,而不是属性的值。
Felix Kling 2012年

1
这是一个数组,而不是对象,对不对?那么,alert(obj)
火箭Hazmat 2012年

Answers:


544

for…in遍历属性名,而不是值,并以未指定的顺序进行迭代(是的,即使在ES6之后也是如此)。您不应该使用它来遍历数组。对于他们来说,有ES5的forEach方法可以将值和索引传递给您提供的函数:

var myArray = [123, 15, 187, 32];

myArray.forEach(function (value, i) {
    console.log('%d: %s', i, value);
});

// Outputs:
// 0: 123
// 1: 15
// 2: 187
// 3: 32

或ES6 Array.prototype.entries,现在已支持当前所有浏览器版本:

for (const [i, value] of myArray.entries()) {
    console.log('%d: %s', i, value);
}

对于一般的可迭代对象(您将使用for…of循环而不是for…in),没有内置函数:

function* enumerate(iterable) {
    let i = 0;

    for (const x of iterable) {
        yield [i, x];
        i++;
    }
}

for (const [i, obj] of enumerate(myArray)) {
    console.log(i, obj);
}

演示

如果您确实是在说for…in-枚举属性-您将需要一个额外的计数器。Object.keys(obj).forEach可以工作,但是只包含自己的属性;for…in在原型链的任何位置都包含可枚举的属性。


2
哦好的。我很困惑。我认为JavaScript的替代与Python的替代是相同的。感谢您的澄清。
hobbes3 2012年

1
@quantumpotato:lets是var具有块范围的。consts不变。
Ry-

1
这是一个详细的答案,谢谢。真正弄清了所有讨论的内容
Dheeraj Bhaskar

1
愚蠢的问题,但是%d和%s实际代表什么,或者它们可以是我想要的任何字母吗?
klewis

2
@klewis:%d格式化整数并%s格式化字符串。它们基于printf。在console.spec.whatwg.org/#formatter上正在制定规范。
Ry-

162

在ES6中,最好将-用于循环。您可以像这样获得索引

for (let [index, val] of array.entries()) {
        // your code goes here    
}

注意,Array.entries()返回一个迭代器,它允许它在for-of循环中工作;不要将此与Object.entries()混淆,后者会返回键值对数组


9
这是一个比公认的更好的答案!
trusktr

3
我认为这种解决方案比forEeach更好。它使用名义上的for ... of循环语法,并且您不必使用单独的函数。换句话说,它在语法上更好。OP似乎想要这个。
u8y7541

1
entries()返回一个空对象:{}。知道为什么会这样吗?我array是一个对象数组。
Joshua Pinter

@JoshuaPinter尝试Object.entries(array)代替array.entries()
tonyg

2
Joshua应该这样做,该对象是一个迭代器,该对象具有一个next()方法,该方法每次调用时都会在数组中返回后续条目。里面没有(可见)数据。您可以通过调用在底层对象中获取数据next(),for-of在幕后进行。cc @tonyg
Shog9

26

这个怎么样

let numbers = [1,2,3,4,5]
numbers.forEach((number, index) => console.log(`${index}:${number}`))

其中array.forEach,该方法具有一index参数,该参数是阵列中当前正在处理的元素的索引。


1
最好的答案在这里
codepleb

4
所选答案发布于此版本的6年之前,并且已经包含了相同的内容……
Deiv

因为break没有可用,所以Foreach不利于优化。
smartworld-dm

19

小数组集合的解决方案:

for (var obj in arr) {
    var i = Object.keys(arr).indexOf(obj);
}

arr -ARRAY, obj-当前元素的键, i -COUNTER / INDEX

注意:方法keys()不适用于<9的IE版本,您应该使用Polyfill代码。 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


7
我建议:改为使用计数器,使其在循环中递增。
mayankcpdixit

2
加上mayankcpdixit,请改用计数器,因为indexOf可能会对性能产生负面影响。
刘恩

1
对象越大,获得的速度越慢。这不会扩展。
dchacke

2
这是一种无谓的缓慢和复杂的,因为var i = 0;i++;更短,更高效。另外,它不适用于不是自己属性的可枚举属性。
Ry-

1
@trusktr:如果需要的话……您仍然不应该使用它。当您更改集合时,只需更改计数器即可。如果不必就地进行,请执行一个不错的功能转换。
Ry-

13

循环中循环访问对象的属性。即使它们有时起作用,也不要将它们用于数组。

这样,对象属性就没有索引,它们都是相等的,并且不需要以确定的顺序进行遍历。如果要对属性进行计数,则必须设置额外的计数器(如在第一个示例中所做的那样)。

遍历数组:

var a = [];
for (var i=0; i<a.length; i++) {
    i // is the index
    a[i] // is the item
}

循环一个对象:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}

3
永远不要(var i=0; i<a.length; i++)浪费资源。使用(var i=0, var len = a.length; i<len; i++)
费利克斯·桑斯

16
@FelixSanz:浪费资源吗?没门。那是几乎没有必要的过早的微优化,并且var i=0; i<a.length; i++)是每一个不错的javascript引擎都会优化的标准循环模式。
Bergi 2014年

3
@FelixSanz:是的,var i=0; i<a.length; i++是最好的做法。
Bergi 2014年

1
KISS。如果您在真正需要这样做的地方编写循环,则可能是做错了什么,或者您对它的必要性有一个比“最佳实践”更好的论据。是的,这是标准做法,但不是针对通用性能优化,而仅是针对微优化。
Bergi 2014年

3
KISS适用于任何地方。过早的优化是一种反实践。
Bergi 2014年

7

正如其他人所说,您不应该使用for..in遍历数组。

for ( var i = 0, len = myArray.length; i < len; i++ ) { ... }

如果您希望语法更简洁,则可以使用forEach:

myArray.forEach( function ( val, i ) { ... } );

如果要使用此方法,请确保包含ES5填充程序以添加对较旧浏览器的支持。


2

rushUp给出的答案是正确的,但这会更方便

for (let [index, val] of array.entries() || []) {
   // your code goes here    
}

1

这是一个eachWithIndex可以处理任何迭代的函数。

您还可以使用编写eachWithKey与objets一起使用的类似函数for...in

// example generator (returns an iterator that can only be iterated once)
function* eachFromTo(start, end) { for (let i = start; i <= end; i++) yield i }

// convers an iterable to an array (potential infinite loop)
function eachToArray(iterable) {
    const result = []
    for (const val of iterable) result.push(val)
    return result
}

// yields every value and index of an iterable (array, generator, ...)
function* eachWithIndex(iterable) {
    const shared = new Array(2)
    shared[1] = 0
    for (shared[0] of iterable) {
        yield shared
        shared[1]++
    }
}

console.log('iterate values and indexes from a generator')
for (const [val, i] of eachWithIndex(eachFromTo(10, 13))) console.log(val, i)

console.log('create an array')
const anArray = eachToArray(eachFromTo(10, 13))
console.log(anArray)

console.log('iterate values and indexes from an array')
for (const [val, i] of eachWithIndex(anArray)) console.log(val, i)

生成器的好处是它们很懒,可以将另一个生成器的结果作为参数。


1

那是我的复合迭代器的版本,它产生一个索引和任何传递的生成器函数的值,并带有(慢)素数搜索的示例:

const eachWithIndex = (iterable) => {
  return {
    *[Symbol.iterator]() {
      let i = 0
      for(let val of iteratable) {
        i++
          yield [i, val]
      }
    }
  }

}

const isPrime = (n) => {
  for (i = 2; i < Math.floor(Math.sqrt(n) + 1); i++) {
    if (n % i == 0) {
      return false
    }
  }
  return true
}

let primes = {
  *[Symbol.iterator]() {
    let candidate = 2
    while (true) {
      if (isPrime(candidate)) yield candidate
        candidate++
    }
  }
}

for (const [i, prime] of eachWithIndex(primes)) {
  console.log(i, prime)
  if (i === 100) break
}


为什么有功能eachWithIndex[Symbol.iterator]而不是只有功能eachWithIndexeachWithIndex不满足可迭代的接口,这是的重点Symbol.iterator
Ry-

@ Ry-好捕获,更改eachWithIndex为接受可迭代并返回封闭的复合可迭代。
akurtser

1

除了每个人发布的非常好的答案之外,我想补充一点,性能最高的解决方案是ES6 entries。对于这里的许多开发人员来说,这似乎是矛盾的,所以我创建了这个perf Benchamrk

在此处输入图片说明

快约6倍。主要是因为不需要:a)多次访问数组,以及b)转换索引。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.