遍历 JavaScript 中的数组

在 Java 中,可以使用for循环遍历数组中的对象,如下所示:

String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray)
{
    // Do something
}

您可以在 JavaScript 中做同样的事情吗?

答案

您有几种选择:

1. 连续for循环:

var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
    console.log(myStringArray[i]);
    //Do something
}

优点

  • 适用于各种环境
  • 您可以使用breakcontinue流控制语句

缺点

  • 太冗长
  • 势在必行
  • 容易出现一对一错误 (有时也称为栅栏错误

2. Array.prototype.forEach

ES5 规范引入了许多有益的数组方法,其中之一是Array.prototype.forEach ,它为我们提供了一种遍历数组的简洁方法:

const array = ["one", "two", "three"]
array.forEach(function (item, index) {
  console.log(item, index);
});

ES5 规范发布时(截至 2009 年 12 月)已经有将近十年的时间,它已经由台式机,服务器和移动环境中的几乎所有现代引擎实现,因此可以安全地使用它们。

借助 ES6 箭头函数语法,它更加简洁:

array.forEach(item => console.log(item));

除非计划支持古老的平台(例如 IE11),否则箭头功能也得到了广泛的实现。你也很安全。

优点

  • 非常简短。
  • 陈述式

缺点

  • 无法使用break / continue

通常,您可以通过在迭代数组元素之前对其进行过滤来替换break命令式循环的需要,例如:

array.filter(item => item.condition < 10)
     .forEach(item => console.log(item))

请记住,如果要迭代一个数组以从中构建另一个数组 ,则应使用map ,我已经看过很多次了这种反模式。

反模式:

const numbers = [1,2,3,4,5], doubled = [];

numbers.forEach((n, i) => { doubled[i] = n * 2 });

map 的正确用例:

const numbers = [1,2,3,4,5];
const doubled = numbers.map(n => n * 2);

console.log(doubled);

另外,例如,如果您试图将数组缩小为一个值,则想对数字数组求和,则应使用reduce方法。

反模式:

const numbers = [1,2,3,4,5];
const sum = 0;
numbers.forEach(num => { sum += num });

正确使用reduce

const numbers = [1,2,3,4,5];
const sum = numbers.reduce((total, n) => total + n, 0);

console.log(sum);

3. ES6 for-of声明

ES6 标准引入了可迭代对象的概念,并定义了用于遍历数据的新构造,即for...of语句。

该语句适用于任何种类的可迭代对象,也适用于生成器(具有[Symbol.iterator]属性的任何对象)。

根据定义,数组对象是 ES6 中内置的可迭代对象,因此可以在它们上使用此语句:

let colors = ['red', 'green', 'blue'];
for (const color of colors){
  console.log(color);
}

优点

  • 它可以遍历各种各样的对象。
  • 可以使用普通的流控制语句( break / continue )。
  • 对迭代串行异步值很有用。

缺点

请勿for...in

@zipcodeman 建议使用for...in语句,但是对于迭代数组for-in应当避免,该语句旨在枚举对象属性。

不应将其用于类似数组的对象,因为:

  • 不能保证迭代的顺序。不能按数字顺序访问数组索引。
  • 继承的属性也被枚举。

第二点是它会给您带来很多问题,例如,如果您扩展Array.prototype对象以在其中包含一个方法,那么该属性也会被枚举。

例如:

Array.prototype.foo = "foo!";
    var array = ['a', 'b', 'c'];
    
    for (var i in array) {
      console.log(array[i]);
    }

上面的代码将控制台日志 “a”,“b”,“c” 和 “foo!”。

如果您使用一些严重依赖本机原型扩充的库(例如,MooTools),则这尤其成问题。

我之前说过的for-in语句用于枚举对象属性,例如:

var obj = {
      "a": 1,
      "b": 2,
      "c": 3
    };

    for (var prop in obj) {
      if (obj.hasOwnProperty(prop)) { 
      // or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
        console.log("prop: " + prop + " value: " + obj[prop])
      }
    }

在上面的示例中, hasOwnProperty方法允许您仅枚举自己的属性 ,仅枚举对象物理上具有的属性,而没有枚举属性。

我建议您阅读以下文章:

是的,假设你实现包括for ... of中介绍的特征的 ECMAScript 2015 年 (以下简称 “和谐” 发布)...... 这是一个非常安全的假设,这些天。

它是这样的:

// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
  // ... do something with s ...
}

甚至更好,因为 ECMAScript 2015 还通过letconst提供了块范围内的变量:

// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
  // ... do something with s ...
}
// s is no longer defined here

关于稀疏数组的注释:JavaScript 中的数组实际上可能不存储其length所报告的项目;该报告的数字仅比存储值的最高索引大一个。如果数组包含的元素少于其长度所指示的元素,则称其为sparse 。例如,拥有仅在索引 3、12 和 247 处有项的数组是完全合法的;该数组的length报告为 248,尽管它实际上仅存储 3 个值。如果您尝试访问任何其他索引处的项目,则该数组似乎在其中具有undefined值。因此,当您要 “遍历” 数组时,您需要回答一个问题:要遍历数组的长度指示的整个范围,并为所有丢失的元素处理undefined s,还是只想处理这些元素实际存在吗?两种方法都有很多应用。这仅取决于您使用数组的目的。

如果你迭代与阵列for .. of ,该循环体被执行length次,循环控制变量被设置为undefined为实际不存在阵列中的任何项目。根据您的 “执行某项操作” 代码的详细信息,该行为可能是您想要的,但是,如果这不是您想要的,则应使用其他方法。

当然,一些开发商别无选择,但无论如何要使用不同的方法,因为不管是什么原因,他们要指定一个 JavaScript 版本尚不支持for ... of

只要你的 JavaScript 实现符合 ECMAScript 规范的前一版本(这就排除了,例如,前 9 Internet Explorer 的版本),那么你可以使用Array#forEach的迭代方法,而不是一个循环。在这种情况下,您传递一个要在数组中每个项目上调用的函数:

var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) { 
     // ... do something with s ...
} );

for ... of不同, .forEach仅为实际包含值的元素调用函数。如果传递带有三个元素且长度为 248 的假设数组,则它将仅调用该函数三次,而不是 248 次。它还可以区分丢失的元素和实际上设置为undefined元素;对于后者,它将仍然调用该函数,并传递undefined作为参数。如果这是你希望如何处理稀疏数组, .forEach可能是,如果你的解释器支持去连路for ... of

所有 JavaScript 版本中可以使用的最后一个选项是显式计数循环 。您只需从 0 到小于长度的 1 进行计数,然后将计数器用作索引。基本循环如下所示:

var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
  s = myStringArray[i];
  // ... do something with s ...
}

这种方法的一个优点是您可以选择如何处理稀疏数组。上面的代码将运行循环的充分的身体length次,用s设置为undefined任何缺失的元素,就像for .. of 。如果您只想处理稀疏数组的实际存在的元素(如.forEach ,则可以在索引上添加一个简单in test:

var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
  if (i in myStringArray) {
    s = myStringArray[i];
    // ... do something with s ...
  }
}

将长度值分配给局部变量(与在循环条件中包含完整的myStringArray.length表达式相反)会在性能上产生重大差异,因为它每次都会跳过属性查找。在我的机器上使用 Rhino,速度提高了 43%。

您可能会在循环初始化子句中看到完成长度缓存,如下所示:

var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {

显式计数循环还意味着您可以访问每个值的索引(如果需要)。索引也作为附加参数传递给传递给forEach的函数,因此您也可以通过以下方式访问它:

myStringArray.forEach( function(s, i) {
   // ... do something with s and i ...
});

for ... of不给你与每个对象相关联的指标,但只要对象你循环访问实际上是一个Arrayfor .. of作品对于其他类型的可迭代可能不具有此方法),您可以使用Array#entries方法将其更改为 [index,item] 对数组,然后对其进行迭代:

for (const [i, s] of myStringArray.entries()) {
  // ... do something with s and i ...
}

其他人提到的for ... in语法用于循环对象的属性;由于 JavaScript 中的 Array 只是具有数字属性名称(和自动更新的length属性)的对象,因此理论上可以使用它遍历 Array。但是问题在于,它不会将自己局限于数字属性值(请记住,即使方法实际上只是其值是闭包的属性),也不能保证以数字顺序遍历那些属性。因此, for ... in语法应用于遍历数组。

您可以使用map ,这是一种功能编程技术,其他语言(例如PythonHaskell)也可以使用map

[1,2,3,4].map( function(item) {
     alert(item);
})

通用语法为:

array.map(func)

通常, func将采用一个参数,该参数是数组的一项。但是对于 JavaScript,它可以采用第二个参数(即项目的索引)和第三个参数(即数组本身)。

array.map的返回值是另一个数组,因此您可以像这样使用它:

var x = [1,2,3,4].map( function(item) {return item * 10;});

现在 x 是[10,20,30,40]

您不必内联编写函数。它可以是一个单独的功能。

var item_processor = function(item) {
      // Do something complicated to an item
}

new_list = my_list.map(item_processor);

这相当于:

for (item in my_list) {item_processor(item);}

除非您没有获得new_list

在 JavaScript 中,不建议使用 for-in 循环遍历数组,但最好使用for循环,例如:

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

它还进行了优化(“缓存” 阵列长度)。如果您想了解更多信息,请阅读我关于该主题的文章

为(myStringArray 的 var){

(直接回答您的问题:现在可以了!)

大多数其他答案都是正确的,但截至本文撰写之时,它们并未提及ECMA Script 6 2015带来了一种进行迭代的新机制,即for..of循环。

这种新语法是在 javascript 中迭代数组的最优雅的方法(只要您不需要迭代索引),但是浏览器尚未广泛支持它。

目前,它可与 Firefox 13 +,Chrome 37 + 一起使用,并且本身无法与其他浏览器一起使用(请参阅下面的浏览器兼容性)。幸运的是,我们拥有 JS 编译器(例如Babel ),它们使我们今天可以使用下一代功能。

它也可以在 Node 上运行(我在 0.12.0 版上进行了测试)。

迭代数组

// You could also use "let" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) { 
   console.log(letter); 
}

迭代对象数组

var band = [
  {firstName : 'John', lastName: 'Lennon'}, 
  {firstName : 'Paul', lastName: 'McCartney'}
];

for(var member of band){
  console.log(member.firstName + ' ' + member.lastName); 
}

迭代生成器:

(示例摘自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

function* fibonacci() { // a generator function
  let [prev, curr] = [1, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  console.log(n);
  // truncate the sequence at 1000
  if (n >= 1000) {
    break;
  }
}

兼容性表: http : //kangax.github.io/es5-compat-table/es6/#For..of 循环

规范: http ://wiki.ecmascript.org/doku.php? id = harmony: iterators

}

Opera,Safari,Firefox 和 Chrome 现在都共享一组增强的 Array 方法,用于优化许多常见循环。

您可能不需要全部,但它们可能非常有用,或者每个浏览器都支持它们。

Mozilla 实验室发布了它们和WebKit都使用的算法,因此您可以自己添加它们。

filter返回满足某些条件或测试的项目数组。

如果每个数组成员通过测试,则每个返回 true。

如果通过测试,则某些返回 true。

forEach在每个数组成员上运行一个函数,并且不返回任何内容。

map类似于 forEach,但是它返回每个元素的操作结果数组。

这些方法的第一个参数都带有一个函数,第二个参数是一个可选参数,第二个参数是一个对象,您希望在它们遍历函数时将其作用域强加给数组成员。

忽略它,直到您需要它为止。

indexOflastIndexOf可以找到第一个或最后一个与其参数完全匹配的元素的适当位置。

(function(){
    var p, ap= Array.prototype, p2={
        filter: function(fun, scope){
            var L= this.length, A= [], i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        val= this[i];
                        if(fun.call(scope, val, i, this)){
                            A[A.length]= val;
                        }
                    }
                    ++i;
                }
            }
            return A;
        },
        every: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && !fun.call(scope, this[i], i, this))
                        return false;
                    ++i;
                }
                return true;
            }
            return null;
        },
        forEach: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
            }
            return this;
        },
        indexOf: function(what, i){
            i= i || 0;
            var L= this.length;
            while(i< L){
                if(this[i]=== what)
                    return i;
                ++i;
            }
            return -1;
        },
        lastIndexOf: function(what, i){
            var L= this.length;
            i= i || L-1;
            if(isNaN(i) || i>= L)
                i= L-1;
            else
                if(i< 0) i += L;
            while(i> -1){
                if(this[i]=== what)
                    return i;
                --i;
            }
            return -1;
        },
        map: function(fun, scope){
            var L= this.length, A= Array(this.length), i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        A[i]= fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
                return A;
            }
        },
        some: function(fun, scope){
            var i= 0, L= this.length;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && fun.call(scope, this[i], i, this))
                        return true;
                    ++i;
                }
                return false;
            }
        }
    }
    for(p in p2){
        if(!ap[p])
            ap[p]= p2[p];
    }
    return true;
})();

使用 while 循环...

var i=0, item, items = ['one','two','three'];
while(item = items[i++]){
    console.log(item);
}

日志:“一个”,“两个”,“三个”

而对于相反的顺序,循环效率更高

var items = ['one','two','three'], i = items.length;
while(i--){
    console.log(items[i]);
}

日志:“三个”,“两个”,“一个”

或经典的for循环

var items = ['one','two','three']
for(var i=0, l = items.length; i < l; i++){
    console.log(items[i]);
}

日志:“一个”,“两个”,“三个”

参考: http : //www.sitepoint.com/google-closure-how-not-to-write-javascript/

介绍

自从我上大学以来,我就使用 Java,JavaScript,Pascal, ABAP ,PHP,Progress 4GL,C / C ++ 以及其他一些我现在无法想到的语言进行编程。

尽管它们都有自己的语言特质,但每种语言都具有许多相同的基本概念。这样的概念包括过程 / 函数, IF语句, FOR循环和WHILE循环。


传统的for循环

传统的for循环包含三个组成部分:

  1. 初始化:在第一次执行 look 块之前执行
  2. 条件:每次在执行循环块之前都要检查条件,如果为 false 则退出循环
  3. 事后思考:循环块执行后每次执行

这三个组成部分彼此隔开;符号。这三个组件中每个组件的内容都是可选的,这意味着以下内容是for循环中最少for

for (;;) {
    // Do stuff
}

当然,您将需要包含if(condition === true) { break; }if(condition === true) { return; }内某处for -loop 得到它停止运行。

通常,尽管如此,初始化用于声明索引,条件用于将索引与最小值或最大值进行比较,事后才用于增加索引:

for (var i = 0, length = 10; i < length; i++) {
    console.log(i);
}

使用传统的for循环遍历数组

遍历数组的传统方法是:

for (var i = 0, length = myArray.length; i < length; i++) {
    console.log(myArray[i]);
}

或者,如果您希望向后循环,请执行以下操作:

for (var i = myArray.length - 1; i > -1; i--) {
    console.log(myArray[i]);
}

但是,可能有多种变体,例如这种变体:

for (var key = 0, value = myArray[key], length = myArray.length; key < length; value = myArray[++key]) {
    console.log(value);
}

... 或者这个...

var i = 0, length = myArray.length;
for (; i < length;) {
    console.log(myArray[i]);
    i++;
}

... 或这个:

var key = 0, value;
for (; value = myArray[key++];){
    console.log(value);
}

哪种效果最好,很大程度上取决于个人喜好和您要实现的特定用例。

请注意,所有浏览器都支持所有这些变体,包括非常古老的浏览器!


while循环

for循环的一种替代方法是while循环。要遍历数组,可以执行以下操作:

var key = 0;
while(value = myArray[key++]){
    console.log(value);
}

像传统的for循环一样,即使最旧的浏览器也支持while循环。

另外,请注意,每个 while 循环都可以重写为for循环。例如, while循环上文的行为与具体的方式,因为这同样for -loop:

for(var key = 0; value = myArray[key++];){
    console.log(value);
}

For...in for...of

在 JavaScript 中,您还可以执行以下操作:

for (i in myArray) {
    console.log(myArray[i]);
}

但是,应谨慎使用,因为在所有情况下,它的行为都与传统的for循环不同,并且需要考虑潜在的副作用。请参阅为什么在数组迭代中使用 “for ... in” 是个坏主意?更多细节。

作为for...in的替代,现在也有for...of 。以下示例显示了for...of循环和for...in循环之间的区别:

var myArray = [3, 5, 7];
myArray.foo = "hello";

for (var i in myArray) {
  console.log(i); // logs 0, 1, 2, "foo"
}

for (var i of myArray) {
  console.log(i); // logs 3, 5, 7
}

此外,您需要考虑到for...of的 Internet Explorer 版本不支持for...of Edge 12+支持), for...in至少需要 Internet Explorer 10 支持。


Array.prototype.forEach()

for -loops 的替代方法是Array.prototype.forEach() ,它使用以下语法:

myArray.forEach(function(value, key, myArray) {
    console.log(value);
});

所有现代浏览器以及 Internet Explorer 9 和更高版本都支持Array.prototype.forEach()


图书馆

最后,许多实用程序库也具有各自的foreach变体。 AFAIK,三种最受欢迎的是:

jQuery.each()jQuery 中

$.each(myArray, function(key, value) {
    console.log(value);
});

_.each()Underscore.js

_.each(myArray, function(value, key, myArray) {
    console.log(value);
});

_.forEach() ,在Lodash.js 中

_.forEach(myArray, function(value, key) {
    console.log(value);
});

如果您想以简洁的方式编写快速循环可以反向进行迭代:

for (var i=myArray.length;i--;){
  var item=myArray[i];
}

这具有缓存长度的好处(类似于for (var i=0, len=myArray.length; i<len; ++i)而不同于for (var i=0; i<myArray.length; ++i) ),同时输入的字符较少。

有时甚至需要反向迭代,例如在活动的 NodeList上迭代时,您计划在迭代过程中从 DOM 中删除项目。

在 JavaScript 中以函数式编程方式遍历数组的一些用例:

1. 遍历数组

const myArray = [{x:100}, {x:200}, {x:300}];

myArray.forEach((element, index, array) => {
    console.log(element.x); // 100, 200, 300
    console.log(index); // 0, 1, 2
    console.log(array); // same myArray object 3 times
});

注意:严格来说,Array.prototype.forEach()并不是一种函数方式,因为它作为输入参数使用的函数不应返回值,因此不能将其视为纯函数。

2. 检查数组中的任何元素是否通过测试

const people = [
    {name: 'John', age: 23}, 
    {name: 'Andrew', age: 3}, 
    {name: 'Peter', age: 8}, 
    {name: 'Hanna', age: 14}, 
    {name: 'Adam', age: 37}];

const anyAdult = people.some(person => person.age >= 18);
console.log(anyAdult); // true

3. 转换为新数组

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => element.x);
console.log(newArray); // [100, 200, 300]

注意:map()方法创建一个新数组,其结果是在调用数组中的每个元素上调用提供的函数。

4. 总结一个特定的属性,并计算其平均值

const myArray = [{x:100}, {x:200}, {x:300}];

const sum = myArray.map(element => element.x).reduce((a, b) => a + b, 0);
console.log(sum); // 600 = 0 + 100 + 200 + 300

const average = sum / myArray.length;
console.log(average); // 200

5. 基于原始数组创建一个新数组,但不对其进行修改

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => {
    return {
        ...element,
        x: element.x * 2
    };
});

console.log(myArray); // [100, 200, 300]
console.log(newArray); // [200, 400, 600]

6. 计算每个类别的数量

const people = [
    {name: 'John', group: 'A'}, 
    {name: 'Andrew', group: 'C'}, 
    {name: 'Peter', group: 'A'}, 
    {name: 'James', group: 'B'}, 
    {name: 'Hanna', group: 'A'}, 
    {name: 'Adam', group: 'B'}];

const groupInfo = people.reduce((groups, person) => {
    const {A = 0, B = 0, C = 0} = groups;
    if (person.group === 'A') {
        return {...groups, A: A + 1};
    } else if (person.group === 'B') {
        return {...groups, B: B + 1};
    } else {
        return {...groups, C: C + 1};
    }
}, {});

console.log(groupInfo); // {A: 3, C: 1, B: 2}

7. 根据特定条件检索数组的子集

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray = myArray.filter(element => element.x > 250);
console.log(newArray); // [{x:300}]

注意:filter()方法创建一个新数组,其中所有元素都通过了由提供的函数实现的测试。

8. 对数组排序

const people = [
  { name: "John", age: 21 },
  { name: "Peter", age: 31 },
  { name: "Andrew", age: 29 },
  { name: "Thomas", age: 25 }
];

let sortByAge = people.sort(function (p1, p2) {
  return p1.age - p2.age;
});

console.log(sortByAge);

在此处输入图片说明

9. 查找数组中的元素

const people = [ {name: "john", age:23},
                {name: "john", age:43},
                {name: "jim", age:101},
                {name: "bob", age:67} ];

const john = people.find(person => person.name === 'john');
console.log(john);

在此处输入图片说明

Array.prototype.find()方法返回满足提供的测试功能的数组中第一个元素的值。

参考文献