在開發過程中經常需要循環遍歷數組或者對象,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
}