forEach、for...in、for...of
forEach
数组实例的遍历方法
const arr=['red', 'green', 'blue']; arr.forEach(function(element, index) { console.log(element);// red green blue console.log(index);// 0 1 2 });
forEach这种写法的问题是,无法中途跳出forEach循环,break命令或return命令都不能奏效。
for...in
JavaScript原有的循环,只能获得对象的键名,不能直接获取键值。
var arr = ['a', 'b', 'c', 'd', 'e']; for(let i in arr) { console.log(i);//0 1 2 3 4 }
for(let index in arr) {
console.log(arr[index]);// a b c d e
}
let es6 = { edition: 6, committee: "TC-39", standard: "ECMA-262" }; for(let e in es6) { console.log(e); } //edition //committee //standard
for...in循环有几个缺点:
数组键名是数字,但是for...in循环是以字符串作为键名"0","1","2"等
for...in循环不仅遍历数字键名,还会遍历手动添加的其它键,甚至包括原型链上的键。
某些情况下,for...in循环会以任意顺序遍历键名。
总之,for...in循环主要是为遍历对象为设计的,不适用于遍历数组。
for...of
ES6借鉴C++、Java、C#和Python语言,引入了for...of循环,作为遍历所有数据结构的统一的方法。一个数据结构只要部署了Symbol.iterator属性,就被视为具有iterator接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。for...of循环可以使用的范围包括数组、Set和Map结构、某些类似数组的对象(比如arguments对象、DOM NodeList对象)、Generator对象以及字符串。
数组原生具有iterator接口(即默认部署了Symbol.iterator属性),for...of循环本质上就是调用这个接口产生的遍历器,允许遍历获得键值;
var arr = ['a', 'b', 'c', 'd', 'e']; for(let i of arr) { console.log(i);//a b c d e }
for...of循环可以代替数组实例的forEach方法
for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性;这一点与for...in也不一样。
let arr = [1,3,5]; arr.foo = 'hello'; for(let i in arr) { console.log(i);//'1', '3', '5', 'foo' } for(let i of arr) { console.log(i);//'1', '3', '5' }
Set和Map结构也原生具有Iterator接口,可以直接使用for...of循环
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
Set和Map遍历的顺序是按照各个成员被添加进数据结构的先后顺序。其次,Set结构遍历,返回的是一个值。Map结构遍历返回的是一个数组,该数组的两个成员分别为当前Map成员的键名和键值。
for...of循环,获得数组的索引,可以借助数组实例的entries方法和keys方法。
let arr = ["a", "b", "c"]; for(let pair of arr.entries()) { console.log(pair); } //[0, 'a'] //[1, 'b'] //[2, 'c']
for...of遍历类似数组的对象(String、DOM NodeList、arguments)
//字符串 let str="hello"; for(let s of str) { console.log(s);// h e l l o } //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循环还能够正确识别32位UTF-16字符
for (let x of 'a\uD83D\uDC0A) { console.log(x); } //'a' //'\uD83D\uDC0A'
并不是所有类似数组的对象都具有Iterator接口,一个方法是用Array.from方法将其转换为数组
let arrayLike = [length: 2, 0: 'a', 1: 'b']; for(let x of array.from(arrayLike)) { console.log(x); }
对于普通的对象,for...of结构不能直接使用,必须部署Iterator接口后才能使用。一种解决方法是将对象的键名生成一个数组,然后遍历这个数组:
for(let key of Object.keys(someObject)) { console.log(key + ":" + someObject[key]); }
另一个方法是使用Generator函数
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
for...of相对其它遍历方法,有以下优点:
有着同for...in一样简洁的语法,但是没有for...in那些缺点;
不同于forEach方法,它可以与break、continue和return配合使用
提供了遍历所有数据结构的统一操作接口
for(var n of fibonacci) { if(n>1000) break; console.log(n); }
参考: