for…inenumerates string keys of enumerable properties, including those inherited through the prototype chain; symbols and non-enumerable properties are skipped. Keys are always strings (array indexes too), integer-like keys come first in ascending order, then other keys in insertion order. It runs onnull/undefinedas a no-op. It is meant for plain-object keys; for arrays it also visits extra properties, and anything added toArray.prototypeshows up.for…ofworks on any iterable (arrays, strings,Map,Set, generators,arguments, DOM collections). It calls[Symbol.iterator]()and gives you the values. A plain object is not iterable, sofor (x of {})throwsTypeError. It supportsbreak,continue,return, andawaitinside anasyncfunction, and on early exit it calls the iterator'sreturn()so generators can clean up.forEachisArray.prototype.forEach(callback)(also onMap,Set,NodeList). It reads indexes0…length-1captured at the start, skips holes, and ignores the callback's return value. You can'tbreak, andreturnonly ends that one callback. It is synchronous: anasynccallback returns a promise thatforEachdiscards, so nothing waits for it and rejections go unhandled.
Pick: values of an array or other iterable, especially with break or await → for…of. Keys of an object → Object.keys/entries with for…of, since for…in includes inherited keys. Plain side effects over a whole array with a short callback → forEach is fine. For async work: sequential → for…of with await; concurrent → Promise.all(array.map(…)).
Variables declared with let/const in a for…in/for…of head get a fresh binding per iteration, so closures capture each iteration's value.