for...in
for...in 语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。
仅迭代自身的属性
如果你只要考虑对象本身的属性,而不是它的原型,那么使用 getOwnPropertyNames() 或执行 hasOwnProperty() 来确定某属性是否是对象本身的属性(也能使用propertyIsEnumerable)。或者,如果你知道不会有任何外部代码干扰,你可以使用检查方法扩展内置原型。
为什么用 for ... in?
for ... in是为遍历对象属性而构建的,不建议与数组一起使用,数组可以用Array.prototype.forEach()和for ... of,那么for ... in的到底有什么用呢?
它最常用的地方应该是用于调试,可以更方便的去检查对象属性(通过输出到控制台或其他方式)。尽管对于处理存储数据,数组更实用些,但是你在处理有key-value数据(比如属性用作“键”),需要检查其中的任何键是否为某值的情况时,还是推荐用for ... in。
示例
下面的函数接受一个对象作为参数。被调用时迭代传入对象的所有可枚举属性然后返回一个所有属性名和其对应值的字符串。
var obj = { a: 1, b: 2, c: 3 }; for (var prop in obj) { console.log("obj." + prop + " = " + obj[prop]); } // Output: // "obj.a = 1" // "obj.b = 2" // "obj.c = 3"
for...of
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
for (variable of iterable) { //statements }
let iterable = [10, 20, 30]; for (let value of iterable) { value += 1; console.log(value); } // 11 // 21 // 31
如果你不想修改语句块中的变量 , 也可以使用const代替let。
let iterable = [10, 20, 30]; for (const value of iterable) { console.log(value); } // 10 // 20 // 30
let iterable = "boo"; for (let value of iterable) { console.log(value); } // "b" // "o" // "o"
let iterable = new Uint8Array([0x00, 0xff]); for (let value of iterable) { console.log(value); } // 0 // 255
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
(function () { for (let argument of arguments) { console.log(argument); } })(1, 2, 3); // 1 // 2 // 3
forEach() Array.prototype.forEach()
forEach() 方法对数组的每个元素执行一次给定的函数。
描述
forEach() 方法是一个迭代方法。它按索引升序地为数组中的每个元素调用一次提供的 callbackFn 函数。与 map() 不同,forEach() 总是返回 undefined,而且不能继续链式调用。其典型的用法是在链式调用的末尾执行某些操作。
callbackFn 仅对已赋值的数组索引调用。对于稀疏数组中的空槽,它不会被调用。
forEach() 不会改变其调用的数组,但是,作为 callbackFn 的函数可以更改数组。请注意,在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:
- 当调用 forEach() 时,callbackFn 不会访问超出数组初始长度的任何元素。
- 已经访问过的索引的更改不会导致 callbackFn 再次调用它们。
- 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。
警告: 上述类型的并发修改经常导致难以理解的代码,通常应避免(特殊情况除外)。
forEach() 方法是通用的。它只期望 this 值具有 length 属性和整数键的属性。
除非抛出异常,否则没有办法停止或中断 forEach() 循环。如果有这样的需求,则不应该使用 forEach() 方法。
可以通过像 for、for...of 和 for...in 这样的循环语句来实现提前终止。当不需要进一步迭代时,诸如 every()、some()、find() 和 findIndex() 等数组方法也会立即停止迭代。
forEach() 期望的是一个同步函数,它不会等待 Promise 兑现。在使用 Promise(或异步函数)作为 forEach 回调时,请确保你意识到这一点可能带来的影响。
const ratings = [5, 4, 5]; let sum = 0; const sumFunction = async (a, b) => a + b; ratings.forEach(async (rating) => { sum = await sumFunction(sum, rating); }); console.log(sum); // 期望的输出:14 // 实际的输出:0
将 for 循环转换为 forEach
const items = ["item1", "item2", "item3"]; const copyItems = []; // before for (let i = 0; i < items.length; i++) { copyItems.push(items[i]); } // after items.forEach((item) => { copyItems.push(item); });
总而言之,言而总之
for...in
let obj = { a: 1, b: 2, c: 3 }; for (let key in obj) { console.log(key); // 输出 "a", "b", "c" }`
for...of
- 用于遍历可迭代对象(如数组、Map、Set 等)。
- 返回的是迭代器的值。
- 可以与 yield 关键字结合使用在生成器函数中。
- 示例:
let arr = [1, 2, 3]; for (let value of arr) { console.log(value); // 输出 "1", "2", "3" }`
forEach
let arr = [1, 2, 3]; arr.forEach((value, index, array) => { console.log(value); // 输出 "1", "2", "3" console.log(index); // 输出 "0", "1", "2" console.log(array); // 输出整个数组 [1, 2, 3] });`