for
循环遍历数组中的对象,如下所示:String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray) {
// Do something
}
我能在JavaScript中做同样的事情吗?
for
循环遍历数组中的对象,如下所示:String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray) {
// Do something
}
我能在JavaScript中做同样的事情吗?
三种主要选项:
for (var i = 0; i < xs.length; i++) { console.log(xs[i]); }
xs.forEach((x, i) => console.log(x));
for (const x of xs) { console.log(x); }
详细的例子如下。
for
循环:var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
console.log(myStringArray[i]);
//Do something
}
优点
break
和continue
流程控制语句缺点
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));
箭头函数也被广泛实现,除非你计划支持古老的平台(例如Internet Explorer 11);否则你也可以安全使用。
优点
缺点
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);
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]);
}
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
方法允许您仅枚举自有属性。也就是说,仅枚举对象实际具有的属性,而不包括继承的属性。forEach
不会等待Promise。当你使用Promise(或async函数)作为forEach
回调时,请确保你了解后果。” (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) - bmaupinfor-of
的缺点:无法获取当前索引。 - Vicfor (const [i, x] of yourArray.entries()) { ... }
。 - Scotty Jamison是的,假设你的实现包括ECMAScript 2015(“Harmony”版本)中引入的for
...of
功能......这在今天是非常安全的假设。
它的工作原理如下:
// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
// ... do something with s ...
}
更好的是,自从 ECMAScript 2015 提供了块级作用域变量后:
// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
// ... do something with s ...
}
// s is no longer defined here
在每次迭代中,变量s
都不同,但只要不在那里修改它,仍然可以在循环体内声明为const
。
关于稀疏数组的说明:JavaScript中的数组可能实际上并不存储与其length
所报告的项数相同的项;该数字仅比存储值的最高索引大1。如果数组保存的元素少于其长度所示,则称其为稀疏。例如,拥有仅在索引3、12和247处具有项目的数组是完全合法的;这样一个数组的length
为248,尽管它实际上只存储了3个值。如果您尝试访问任何其他索引处的项,则该数组将显示为具有undefined
值,但该数组仍然与实际上具有存储undefined
值的数组不同。您可以以多种方式看到此差异,例如在Node REPL中显示数组的方式:
> a // array with only one item, at index 12
[ <12 empty items>, 1 ]
> a[0] // appears to have undefined at index 0
undefined
> a[0]=undefined // but if we put an actual undefined there
undefined
> a // it now looks like this
[ undefined, <11 empty items>, 1 ]
undefined
,还是只想处理实际存在的元素?这两种方法都有很多应用;它只取决于你使用数组的方式。for
..of
迭代一个数组,循环体会被执行length
次,并且循环控制变量会被设置为undefined
,对于数组中实际不存在的项。根据你的“做某事”的代码的细节,那种行为可能是你想要的,但如果不是,你应该使用不同的方法。for
...of
的JavaScript版本进行开发。Array#forEach
迭代器方法而不是循环。在这种情况下,您需要传递一个函数,以便在数组中的每个项上调用该函数:var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) {
// ... do something with s ...
} );
如果您的实现支持ES6 +,当然可以使用箭头函数:
myStringArray.forEach( s => {
// ... do something with s ...
} );
for
...of
不同,.forEach
仅调用实际存在于数组中的元素的函数。如果传递具有三个元素和长度为248的假想数组,则它仅调用函数三次,而不是248次。如果这是您想要处理稀疏数组的方式,则即使您的解释器支持for
...of
,.forEach
也可能是一种方法。
最后一个选项,在所有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
测试: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
表达式)可以显著提高性能,因为它可以跳过每次查找属性。您可能会在循环初始化子句中看到长度缓存,如下所示:var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {
forEach
的函数,因此您也可以通过这种方式访问它:myStringArray.forEach( (s,i) => {
// ... do something with s and i ...
});
for
...of
不能给你每个对象关联的索引,但只要你迭代的对象实际上是Array
的一个实例(而不是其他可迭代类型for
..of
可以使用的),你可以使用Array#entries方法将其更改为[index,item]对的数组,然后迭代它:
for (const [i, s] of myStringArray.entries()) {
// ... do something with s and i ...
}
for
...in
语法是用于循环遍历对象属性的;由于JavaScript中的数组只是一个具有数值属性名称(以及自动更新的length
属性)的对象,因此理论上可以使用它来循环遍历数组。但问题在于它不仅限于数值属性值(请记住,即使方法实际上只是其值为闭包的属性),而且不能保证按数值顺序迭代。因此,for
...in
语法不应用于循环遍历数组。map
,这是一种函数式编程技术,也适用于其他语言,例如Python和Haskell。[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
。
Array.forEach
来实现。map
是用于生成新数组的。 - harto(直接回答你的问题:现在可以了!)
其他回答都是正确的,但它们没有提到(截至本文写作时)ECMAScript 6 2015将带来一种新的迭代机制,即for..of
循环。
这个新语法是JavaScript中迭代数组最优雅的方式(只要不需要迭代索引)。
它目前支持Firefox 13+和Chrome 37+,并且它在其他浏览器上不能自动工作(请参见下面的浏览器兼容性)。幸运的是,我们有JavaScript编译器(例如Babel),可以让我们使用未来的特性。
它还适用于Node.js(我在版本0.12.0上进行了测试)。
迭代一个数组
// You could also use "let" or "const" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) {
console.log(letter);
}
迭代对象数组
const band = [
{firstName : 'John', lastName: 'Lennon'},
{firstName : 'Paul', lastName: 'McCartney'}
];
for(const 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 (const n of fibonacci()) {
console.log(n);
// Truncate the sequence at 1000
if (n >= 1000) {
break;
}
}
兼容性表格: http://kangax.github.io/compat-table/es6/#test-for..of_loops
规范: http://wiki.ecmascript.org/doku.php?id=harmony:iterators
在JavaScript中,不建议使用for-in循环遍历数组,而是最好使用for
循环,例如:
for(var i=0, len=myArray.length; i < len; i++){}
它也经过了优化("缓存"数组长度)。如果您想了解更多,请阅读我在这个主题上的文章。
您可以通过许多不同的方法遍历数组。我已经按照我最喜欢的6种方法从上到下排序。
当只需要简单地遍历数组时,for
循环是我的首选。
let array = [1, 2, 3, 4, 5];
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
forEach
循环是一种现代的遍历数组的方式。此外,它还提供了更多的灵活性和对数组和元素的控制。
let array = [1, 2, 3, 4, 5];
array.forEach((element) => {
console.log(element);
});
for...of
循环直接让你访问数组元素。
let array = [1, 2, 3, 4, 5];
for (let element of array) {
console.log(element);
}
for...in
循环可以提供一个键,通过该键可以访问数组元素。
let array = [1, 2, 3, 4, 5];
for(let index in array){
console.log(array[index]);
}
while循环也可以用来遍历数组。
let array = [1, 2, 3, 4, 5];
let length = array.length;
while(length > 0){
console.log(array[array.length - length]);
length--;
}
同样地,我使用 do...while 循环
let array = [1, 2, 3, 4, 5];
let length = array.length;
do {
console.log(array[array.length - length]);
length--;
}
while (length > 0)
现在,Opera、Safari、Firefox和Chrome共享一组增强的数组方法以优化许多常见的循环操作。
您可能不需要使用全部这些方法,但它们非常有用,如果每个浏览器都支持它们就更好了。
Mozilla Labs发布了它们和 WebKit 都在使用的算法,因此您可以自己添加它们。
filter 返回满足某些条件或测试的项目数组。
every 如果每个数组成员都通过了测试,则返回true。
some 如果任何一个成员通过了测试,则返回true。
forEach 对每个数组成员运行一个函数并且不返回任何值。
map 类似于forEach,但它返回一个结果数组,该数组包含每个元素操作的结果。
这些方法都采用函数作为它们的第一个参数,并具有可选的第二个参数,该参数是一个对象,您希望在函数循环遍历数组成员时对其施加作用域。
在您需要它之前请忽略它。
indexOf 和 lastIndexOf 找到与其参数完全匹配的第一个或最后一个元素的适当位置。
(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;
})();
自我大学以来,我就会使用Java、JavaScript、Pascal、ABAP、PHP、Progress 4GL、C/C++等编程语言,可能还有其他一些语言现在想不起来了。
虽然它们都有自己的语言特点,但是这些语言共享许多相同的基本概念,如过程/函数、IF
语句、FOR
循环和WHILE
循环。
for
循环传统的for
循环有三个组成部分:
这三个组成部分由;
符号分隔。每个组成部分的内容都是可选的,这意味着以下是可能的最简单的for
循环:
for (;;) {
// Do stuff
}
if(condition === true) { break; }
或者 if(condition === true) { return; }
来使其停止运行。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
循环while
循环是for
循环的一种替代方案。要遍历数组,你可以这样做:
var key = 0;
while(value = myArray[key++]){
console.log(value);
}
与传统的for
循环一样,即使是最古老的浏览器也支持while
循环。
此外,请注意,每个while
循环都可以重写为for
循环。例如,此处的while
循环的行为与此for
循环相同:
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
}
另外,您需要考虑到Internet Explorer的任何版本都不支持for...of
(Edge 12+支持),而且for...in
至少需要Internet Explorer 10。
Array.prototype.forEach()
for
循环的替代方法是Array.prototype.forEach()
,它使用以下语法:
myArray.forEach(function(value, key, myArray) {
console.log(value);
});
Array.prototype.forEach()
被所有现代浏览器支持,包括Internet Explorer 9及更高版本。
最后,许多实用程序库也有自己的foreach
变体。据我所知,最流行的三个是这些:
$.each(myArray, function(key, value) {
console.log(value);
});
_.each(myArray, function(value, key, myArray) {
console.log(value);
});
_.forEach()
, 在 Lodash 中:
_.forEach(myArray, function(value, key) {
console.log(value);
});
使用while循环...
var i = 0, item, items = ['one', 'two', 'three'];
while(item = items[i++]){
console.log(item);
}
它记录下了: 'one', 'two' 和 'three'
而倒序打印,则可以使用更加高效的循环:
var items = ['one', 'two', 'three'], i = items.length;
while(i--){
console.log(items[i]);
}
它记录:'three','two'和'one'
或者传统的for
循环:
var items = ['one', 'two', 'three']
for(var i=0, l = items.length; i < l; i++){
console.log(items[i]);
}
它记录了:'one','two','three'
使用函数式编程的方式在JavaScript中循环遍历数组的一些用例:
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()严格来说不是函数式编程,因为它所接受的输入参数函数不应该返回一个值,因此不能被视为纯函数。
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
const myArray = [{x:100}, {x:200}, {x:300}];
const newArray= myArray.map(element => element.x);
console.log(newArray); // [100, 200, 300]
注意:map() 方法会对调用数组中的每个元素执行提供的函数,并使用函数返回值创建一个新数组。
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
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]
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}
const myArray = [{x:100}, {x:200}, {x:300}];
const newArray = myArray.filter(element => element.x > 250);
console.log(newArray); // [{x:300}]
注意:filter() 方法通过提供的函数实现测试,创建一个新数组,其中包含所有通过测试的元素。
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);
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()方法返回数组中第一个满足所提供测试函数的元素的值。
for-in
循环枚举对象属性时没有特定的顺序,并且也枚举继承的属性...对于迭代数组,始终建议使用连续的循环。 - Christian C. Salvadóin
和of
,它们有不同的用法。此外,你还需要使用forEach
和丑陋而烦人的基于索引的循环方式。其他现代语言循环集合都很容易和直观,没有任何意外或困惑。JS也可以做到,但它并没有。 - jpmc26