0%

js中的几种for循环

在JS刚出来的时候,想要遍历一个数组,可以像下面这样:

1
2
3
for (var i = 0; i < array.length; i++) {
conosole.log(array[i])
}

forEach

在ES5之后,新加了一个forEach方法:

1
2
3
array.forEach(function (item) {
console.log(item)
})

forEach的写法比原来的for循环简洁,但是在其内部,却不能使用breakreturn结束循环以及continue跳过循环,比如:

1
2
3
4
5
6
7
[1, 2, 3].forEach(function(item){
if(item === 2){
return
// 使用 break 及 continue 均会报错
}
console.log(item) // 1 3
})

for…in

ES5中,还有一个for...in循环,其用法如下:

1
2
3
for (var index in array) {
console.log(array[index])
}

但是,for...in有以下几个特点:

  1. 枚举的索引index是字符型,不能直接进行数字运算,如:
1
2
3
4
5
var sum = 0
for (var index in [1, 2, 3]) {
sum += index
}
console.log(sum) // '0012', 而不是 3
  1. 会将原型对象也一起枚举出来,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

var arr = [3, 5, 7];
arr.foo = 'bar';

for (var i in arr) {
console.log(i);
}
// 输出:
// 0
// 1
// 2
// foo
// arrCustom
// objCustom

如果不想枚举出原型对象,可以配合使用hasOwnProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

var arr = [3, 5, 7];
arr.foo = 'bar';

for (var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i)
}
}
// 输出:
// 0
// 1
// 2
// foo
  1. 循环出来的属性顺序如下:先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来。

整数属性满足 String(Math.trunc(Number(prop)) === prop

“49” 是整数属性,因为 String(Math.trunc(Number('49')) 的结果还是 “49”。
“+49” 不是整数属性,因为 String(Math.trunc(Number('+49')) 的结果是 “49”,不是 “+49”。
“1.2” 不是整数属性,因为 String(Math.trunc(Number('1.2')) 的结果是 “1”,不是 “1.2”。

比如:

1
2
3
4
5
6
7
8
9
10
var codes = {
"49": "Germany",
"41": "Switzerland",
"44": "Great Britain",
"1": "USA"
}

for(var code in codes) {
console.log(code) // 1, 41, 44, 49
}

for…of

在ES6中,新加了一个for...of循环,其用法如下:

1
2
3
for (var value of array) {
console.log(value)
}

与前面几种方式比较,for...of是循环数组元素最简洁的方法,避免了for...in的几个不足,同时也能够使用returnbreakcontinue

for...of能够使用的范围包括了数组、SetMap、字符串、类数组对象(如argumentsDOM NodeList对象)、 Generator对象。

数组

for...of能够直接使用在数组上,其遍历得到的是键值,而不像for...in一样得到的是键名:

1
2
3
4
5
6
7
8
9
var arr = ['a', 'b', 'c', 'd']

for (var index in arr) {
console.log(index) // 0 1 2 3
}

for (var value of arr) {
console.log(value) // a b c d
}

同时,for...of只会遍历数组的数字索引的属性,而for...in会遍历所有属性:

1
2
3
4
5
6
7
8
9
10
var arr = [3, 5, 7]
arr.foo = 'bar'

for (var index in arr) {
console.log(index) // "0", "1", "2", "foo"
}

for (var value of arr) {
console.log(value) // "3", "5", "7"
}

Set、Map

Set、Map在使用for...of进行遍历的时候,顺序是按照各个成员添加到数据结构中的顺序。另外,Set 遍历时,返回的是一个值,而 Map 遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"])
for (var e of engines) {
console.log(e)
}
// Gecko
// Trident
// Webkit

var es6 = new Map()
es6.set("edition", 6)
es6.set("committee", "TC39")
es6.set("standard", "ECMA-262")
for (var [name, value] of es6) {
console.log(name + ": " + value)
}
// edition: 6
// committee: TC39
// standard: ECMA-262

for (var pair of es6) {
console.log(pair)
}
// ['edition', 6]
// ['committee', TC39]
// ['standard', ECMA-262]

字符串

在使用for...of遍历字符串时,还能够正确的识别Unicode编码:

1
2
3
4
5
6
for (var chr of "😺😲") {
console.log(chr) // 😺, 😲
}
for (var chr in "😺😲") {
console.log(chr) // '0', '1', '2', '3' <--- 将2个Unicode字符识别成了4个字符
}

类数组对象

类数组对象的遍历方法也和数组一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// DOM NodeList对象
let paras = document.querySelectorAll("p");

for (let p of paras) {
p.classList.add("test");
}

// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// 'a'
// 'b'

普通对象

for...of并不能像for...in一样直接遍历普通对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
}

for (var e in es6) {
console.log(e)
}
// edition
// committee
// standard

for (var e of es6) {
console.log(e)
}
// TypeError: es6 is not iterable

解决方法有两种,一是使用Object.entries/Object.keys/Object.values方法由普通对象生成键值对/键名/键值数组,然后遍历这个数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
}
for (var e of Object.entries(es6)) {
console.log(e)
}
// ["edition", 6]
// ["committee", "TC39"]
// ["standard", "ECMA-262"]

for (var e of Object.keys(es6)) {
console.log(e + ': ' + es6[e])
}
// edition: 6
// committee: TC39
// standard: ECMA-262

for (var e of Object.values(es6)) {
console.log(e)
}
// 6
// TC39
// ECMA-262

另一种是使用Generator函数将对象包装一下:

1
2
3
4
5
6
7
8
9
10
11
12
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}

for (let [key, value] of entries(obj)) {
console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3

参考

  1. ES6 In Depth: Iterators and the for-of loop
  2. Iterator 和 for…of 循环
  3. http://javascript.info/object#the-for-in-loop