在开发过程中经常需要循环遍历数组或者对象,js也为我们提供了不少方法供使用,其中就有三兄弟forEach、for...in、for...of,这三个方法应该是使用频率最高的,但很多人却一值傻傻分不清,经常该混淆了它们的功能和注意点。就在今天,我来给它们一个大区分(*・ω< )。
forEach
forEach() 方法对数组的每个元素执行一次提供的函数。
从ES5开始,Javascript内置了forEach方法,用来遍历数组 。
var arr = [\'a\', \'b\', \'c\', \'d\', \'e\']; arr.forEach(function(item) { console.log(item); // a,b,c,d,e });
!注意:forEach方法没办法使用
break
语句跳出循环,或者使用return
从函数体内返回。
for...in
for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。
语法:
for (variable in object) {...}variable 在每次迭代时,将不同的属性名分配给变量。
object 被迭代枚举其属性的对象。
// 例子一 let obj = {a: \'1\', b: \'2\', c: \'3\', d: \'4\'}; for (let o in obj) { console.log(o) // 遍历的实际上是对象的属性名称 a,b,c,d console.log(obj[o]) // 这个才是属性对应的值1,2,3,4 } // 例子二 Object.prototype.objName = \'objName \'; var obj = {a: \'1\', b: \'2\', c: \'3\', d: \'4\'}; for (let o in obj) { console.log(o) // a,b,c,d,objName }
for...in 循环只遍历可枚举属性。像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf() 方法或 Object的toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。
! 注意:建议不使用for...in去迭代一个Array。因为设计之初,是给普通以字符串的值为key的对象使用的,而非数组。
数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。使用不能保证for...in将以任何特定的顺序返回索引。因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。因此当迭代访问顺序很重要的数组时,最好用整数索引去进行for循环(或者使用 Array.prototype.forEach() 或 for...of 循环)。
对于for...in的循环,可以由break,throw终止。
var obj = {a: \'1\', b: \'2\', c: \'3\', d: \'4\'} for (let o in obj) { if(o==\'c\'){ break; } console.log(o); } // 输出: a,b
for...of
for...of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
语法:
for (variable of iterable) { //statements }variable 在每次迭代中,将不同属性的值分配给变量。
iterable 被迭代枚举其属性的对象。
迭代数组Array
let iterable = [10, 20, 30]; for (let value of iterable) { console.log(value); } // 10 // 20 // 30
迭代字符串String
let iterable = \"boo\"; for (let value of iterable) { console.log(value); } // \"b\" // \"o\" // \"o\"
迭代Map
let iterable = new Map([[\"a\", 1], [\"b\", 2], [\"c\", 3]]); for (let entry of iterable) { console.log(entry); } // [\"a\", 1] // [\"b\", 2] // [\"c\", 3] for (let [key, value] of iterable) { console.log(value); } // 1 // 2 // 3
迭代Set
let iterable = new Set([1, 1, 2, 2, 3, 3]); for (let value of iterable) { console.log(value); } // 1 // 2 // 3
对于for...of
的循环,可以由break
, throw
或return
终止。在这些情况下,迭代器关闭
function* foo(){ yield 1; yield 2; yield 3; }; for (let o of foo()) { console.log(o); break; // closes iterator, triggers return }
for...of与for...in的区别
无论是for...in还是for...of语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。
for...in 语句以原始插入顺序迭代对象的可枚举属性。
for...of 语句遍历可迭代对象定义要迭代的数据。
以下示例显示了与Array一起使用时,for...of循环和for...in循环之间的区别。
Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {}; // 每个对象将继承objCustom属性,并且作为Array的每个对象将继承arrCustom属性, // 因为将这些属性添加到Object.prototype和Array.prototype。 // 由于继承和原型链,对象iterable继承属性objCustom和arrCustom。 let iterable = [3, 5, 7]; iterable.foo = \'hello world\'; // 此循环仅以原始插入顺序记录iterable对象的可枚举属性。 // 它不记录数组元素3, 5, 7 或hello,因为这些不是枚举属性。 // 但是它记录了数组索引以及arrCustom和objCustom。 // 前面说了,不建议使用for...in迭代数组,这里是纯粹举例才这样写,请勿模仿 for (let i in iterable) { console.log(i); // 0, 1, 2, \"foo\", \"arrCustom\", \"objCustom\" } // 这个循环类似于第一个,但是它使用hasOwnProperty() 来检查, // 如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。 // 记录的属性是0, 1, 2和foo,因为它们是自身的属性(不是继承的)。 // 属性arrCustom和objCustom不会被记录,因为它们是继承的。 for (let i in iterable) { if (iterable.hasOwnProperty(i)) { console.log(i); // 0, 1, 2, \"foo\" } } // 该循环迭代并记录iterable作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性。 for (let i of iterable) { console.log(i); // 3, 5, 7 }
在上面可以粗略看到,for...in循环的是对象的键(key),而for...of则是对象的值。
除此之外,for...of 不能循环非iterable对象。
let newObj = {a: \'1\', b: \'2\', c: \'3\', d: \'4\'}; for (let o of newObj) { console.log(o); // Uncaught TypeError: newObj is not iterable }