二十一 個 JavaScript 迴圈遍歷方法,你都知道嗎?

語言: CN / TW / HK

今天我們來看點基礎知識,看看JavaScript中的那些迴圈遍歷方法:

一、陣列遍歷方法

1. forEach()

forEach 方法用於呼叫陣列的每個元素,並將元素傳遞給回撥函式。陣列中的每個值都會呼叫回撥函式。其語法如下:

array.forEach(function(currentValue, index, arr), thisValue) 

該方法的第一個引數為回撥函式,是必傳的,它有三個引數:

  •  currentValue:必需。當前元素
  •  index:可選。當前元素的索引值。
  •  arr:可選。當前元素所屬的陣列物件 
let arr = [1,2,3,4,5]  
arr.forEach((item, index, arr) => {  
  console.log(index+":"+item)  
}) 

該方法還可以有第二個引數,用來繫結回撥函式內部this變數(前提是回撥函式不能是箭頭函式,因為箭頭函式沒有this):

let arr = [1,2,3,4,5]  
let arr1 = [9,8,7,6,5]  
arr.forEach(function(item, index, arr){ 
  console.log(this[index])  //  9 8 7 6 5  
}, arr1) 

注意:

  •  forEach 方法不會改變原陣列,也沒有返回值;
  •  forEach無法使用 break,continue 跳出迴圈,使用 return 時,效果和在 for 迴圈中使用 continue 一致;
  •  forEach 方法無法遍歷物件,僅適用於陣列的遍歷。

2. map()

map() 方法會返回一個新陣列,陣列中的元素為原始陣列元素呼叫函式處理後的值。該方法按照原始陣列元素順序依次處理元素。其語法如下:

array.map(function(currentValue,index,arr), thisValue) 

該方法的第一個引數為回撥函式,是必傳的,它有三個引數:

  •  currentValue:必須。當前元素的值;
  •  index:可選。當前元素的索引值;
  •  arr:可選。當前元素屬於的陣列物件。 
let arr = [1, 2, 3];   
arr.map(item => {  
    return item + 1;  
})  
// 輸出結果: [2, 3, 4] 

該方法的第二個引數用來繫結引數函式內部的this變數,是可選的:

let arr = ['a', 'b', 'c'];   
[1, 2].map(function (e) {  
    return this[e];  
}, arr)  
// 輸出結果: ['b', 'c'] 

該方法還可以進行鏈式呼叫:

let arr = [1, 2, 3];   
arr.map(item => item + 1).map(item => item + 1)  
// 輸出結果: [3, 4, 5] 

注意:

  •  map 方法不會對空陣列進行檢測;
  •  map 方法遍歷陣列時會返回一個新陣列,不會改變原始陣列;
  •  map 方法有返回值,可以return出來,map的回撥函式中支援return返回值;
  •  map 方法無法遍歷物件,僅適用於陣列的遍歷。

3. for of

for...of 語句建立一個迴圈來迭代可迭代的物件。在 ES6 中引入的 for...of 迴圈,以替代 for...in 和 forEach() ,並支援新的迭代協議。其語法如下:

for (variable of iterable) {  
    statement  
} 

該方法有兩個引數:

  •  variable:每個迭代的屬性值被分配給該變數。
  •  iterable:一個具有可列舉屬性並且可以迭代的物件。

該方法可以獲取陣列的每一項:

let arr = [  
    {id:1, value:'hello'},  
    {id:2, value:'world'},  
    {id:3, value:'JavaScript'}  
]  
for (let item of arr) { 
  console.log(item);   
}  
// 輸出結果:{id:1, value:'hello'}  {id:2, value:'world'} {id:3, value:'JavaScript'} 

注意:

  •  for of 方法只會遍歷當前物件的屬性,不會遍歷其原型鏈上的屬性;
  •  for of 方法適用遍歷 陣列/ 類陣列/字串/map/set 等擁有迭代器物件的集合;
  •  for of 方法不支援遍歷普通物件,因為其沒有迭代器物件。如果想要遍歷一個物件的屬性,可以用 for in 方法;
  •  可以使用break、continue、return來中斷迴圈遍歷;

4. filter()

filter()方法用於過濾陣列,滿足條件的元素會被返回。它的引數是一個回撥函式,所有陣列元素依次執行該函式,返回結果為true的元素會被返回,如果沒有符合條件的元素,則返回空陣列。其語法如下:

array.filter(function(currentValue,index,arr), thisValue) 

該方法的第一個引數為回撥函式,是必傳的,它有三個引數:

  •  currentValue:必須。當前元素的值;
  •  index:可選。當前元素的索引值;
  •  arr:可選。當前元素屬於的陣列物件。 
const arr = [1, 2, 3, 4, 5]  
arr.filter(item => item > 2)   
// 輸出結果:[3, 4, 5] 

同樣,它也有第二個引數,用來繫結引數函式內部的this變數。

可以使用filter()方法來移除陣列中的undefined、null、NAN等值:

let arr = [1, undefined, 2, null, 3, false, '', 4, 0]  
arr.filter(Boolean)  
// 輸出結果:[1, 2, 3, 4] 

注意:

  •  filter 方法會返回一個新的陣列,不會改變原陣列;
  •  filter 方法不會對空陣列進行檢測;
  •  filter 方法僅適用於檢測陣列。

5. some()、every()

some() 方法會對陣列中的每一項進行遍歷,只要有一個元素符合條件,就返回true,且剩餘的元素不會再進行檢測,否則就返回false。

every() 方法會對陣列中的每一項進行遍歷,只有所有元素都符合條件時,才返回true,如果陣列中檢測到有一個元素不滿足,則整個表示式返回 false ,且剩餘的元素不會再進行檢測。其語法如下:

兩者的語法如下:

array.some(function(currentValue,index,arr),thisValue)  
array.every(function(currentValue,index,arr), thisValue) 

兩個方法的第一個引數為回撥函式,是必傳的,它有三個引數:

  •  currentValue:必須。當前元素的值;
  •  index:可選。當前元素的索引值;
  •  arr:可選。當前元素屬於的陣列物件。 
let arr = [1, 2, 3, 4, 5]  
arr.some(item => item > 4)   
// 輸出結果:true  
let arr = [1, 2, 3, 4, 5]  
arr.every(item => item > 0)   
// 輸出結果:true 

注意:

  •  兩個方法都不會改變原陣列,會返回一個布林值;
  •  兩個方法都不會對空陣列進行檢測;
  •  兩個方法都僅適用於檢測陣列。

6. reduce()、reduceRight()

reduce() 方法接收一個函式作為累加器,陣列中的每個值(從左到右)開始縮減,最終計算為一個值。reduce() 可以作為一個高階函式,用於函式的 compose。其語法如下:

array.reduce(function(total, currentValue, currentIndex, arr), initialValue) 

reduce 方法會為陣列中的每一個元素依次執行回撥函式,不包括陣列中被刪除或從未被賦值的元素,回撥函式接受四個引數:

  •  total:上一次呼叫回撥返回的值,或者是提供的初始值(initialValue);
  •  currentValue:當前被處理的元素;
  •  currentIndex:當前元素的索引;
  •  arr:當前元素所屬的陣列物件。

該方法的第二個引數是 initialValue,表示傳遞給函式的初始值 (作為第一次呼叫 callback 的第一個引數):

let arr = [1, 2, 3, 4]  
let sum = arr.reduce((prev, cur, index, arr) => {  
    console.log(prev, cur, index);  
    return prev + cur;  
})  
console.log(arr, sum); 

輸出結果:

1 2 1  
3 3 2  
6 4 3  
[1, 2, 3, 4] 10 

再來加一個初始值試試:

let arr = [1, 2, 3, 4]  
let sum = arr.reduce((prev, cur, index, arr) => {  
    console.log(prev, cur, index);  
    return prev + cur;  
}, 5)  
console.log(arr, sum); 

輸出結果:

5 1 0  
6 2 1  
8 3 2  
11 4 3  
[1, 2, 3, 4] 15 

由此可以得出結論:如果沒有提供初始值initialValue,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供了初始值initialValue,從索引0開始執行

reduceRight() 方法和的reduce()用法幾乎一致,只是該方法是對陣列進行倒序遍歷的,而reduce()方法是正序遍歷的。

let arr = [1, 2, 3, 4]  
let sum = arr.reduceRight((prev, cur, index, arr) => {  
    console.log(prev, cur, index);  
    return prev + cur;  
}, 5)  
console.log(arr, sum); 

輸出結果:

5 4 3  
9 3 2  
12 2 1  
14 1 0  
[1, 2, 3, 4] 15 

注意:

  •  兩個方法都不會改變原陣列;
  •  兩個方法如果新增初始值,就會改變原陣列,會將這個初始值放在陣列的最後一位;
  •  兩個方法對於空陣列是不會執行回撥函式的。

7. find()、findIndex()

find() 方法返回通過函式內判斷的陣列的第一個元素的值。當陣列中的元素在測試條件時返回 true 時, find() 返回符合條件的元素,之後的值不會再呼叫執行函式。如果沒有符合條件的元素返回 undefined。

findIndex() 方法返回傳入一個測試函式符合條件的陣列第一個元素位置(索引)。當陣列中的元素在函式條件時返回 true 時, findIndex() 返回符合條件的元素的索引位置,之後的值不會再呼叫執行函式。如果沒有符合條件的元素返回 -1。

兩個方法的語法如下:

array.find(function(currentValue, index, arr),thisValue)  
array.findIndex(function(currentValue, index, arr), thisValue) 

兩個方法的第一個引數為回撥函式,是必傳的,它有三個引數:

  •  currentValue:必需。當前元素;
  •  index:可選。當前元素的索引;
  •  arr:可選。當前元素所屬的陣列物件。 
let arr = [1, 2, 3, 4, 5]  
arr.find(item => item > 2)   
// 輸出結果:3  
let arr = [1, 2, 3, 4, 5]  
arr.findIndex(item => item > 2)   
// 輸出結果:2 

find()和findIndex()兩個方法幾乎一樣,只是返回結果不同:

  •  find():返回的是第一個符合條件的值;
  •  findIndex:返回的是第一個返回條件的值的索引值。

注意:

  •  兩個方法對於空陣列,函式是不會執行的;
  •  兩個方法否不會改變原陣列。

8. keys()、values()、entries()

三個方法都返回一個數組的迭代物件,物件的內容不太相同:

  •  keys() 返回陣列的索引值;
  •  values() 返回陣列的元素;
  •  entries() 返回陣列的鍵值對。

三個方法的語法如下:

array.keys()  
array.values()  
array.entries() 

這三個方法都沒有引數:

let arr = ["Banana", "Orange", "Apple", "Mango"];  
const iterator1 = arr.keys();  
const iterator2 = arr.values()   
const iterator3 = arr.entries()   
for (let item of iterator1) {  
  console.log(item);  
}  
// 輸出結果: 0 1 2 3  
for (let item of iterator2) { 
  console.log(item);  
}  
// 輸出結果:Banana Orange Apple Mango  
for (let item of iterator3) {  
  console.log(item);  
}  
// 輸出結果:[0, 'Banana'] [1, 'Orange'] [2, 'Apple'] [3, 'Mango'] 

總結:

方法 是否改變原陣列 特點
forEach() 沒有返回值
map() 有返回值,可鏈式呼叫
for of for...of遍歷具有Iterator迭代器的物件的屬性,返回的是陣列的元素、物件的屬性值,不能遍歷普通的obj物件,將非同步迴圈變成同步迴圈
filter() 過濾陣列,返回包含符合條件的元素的陣列,可鏈式呼叫
every()、some() some()只要有一個是true,便返回true;而every()只要有一個是false,便返回false.
find()、findIndex() find()返回的是第一個符合條件的值;findIndex()返回的是第一個返回條件的值的索引值
reduce()、reduceRight() reduce()對陣列正序操作;reduceRight()對陣列逆序操作
keys()、values()、entries() keys() 返回陣列的索引值;values() 返回陣列元素;entries() 返回陣列的鍵值對。

二、物件遍歷方法

1. for in

for…in 主要用於迴圈物件屬性。迴圈中的程式碼每執行一次,就會對物件的屬性進行一次操作。其語法如下:

for (var in object) {  
 執行的程式碼塊  
} 

其中兩個引數:

  •  var:必須。指定的變數可以是陣列元素,也可以是物件的屬性。
  •  object:必須。指定迭代的的物件。 
var obj = {a: 1, b: 2, c: 3};    
for (var i in obj) {  
    console.log('鍵名:', i);   
    console.log('鍵值:', obj[i]);   
} 

輸出結果:

鍵名:a  
鍵值: 1  
鍵名:b  
鍵值: 2  
鍵名:c  
鍵值: 3 

注意:

for in 方法不僅會遍歷當前的物件所有的可列舉屬性,還會遍歷其原型鏈上的屬性。

2. Object.keys()、Object.values()、Object.entries()

這三個方法都用來遍歷物件,它會返回一個由給定物件的自身可列舉屬性(不含繼承的和Symbol屬性)組成的陣列,陣列元素的排列順序和正常迴圈遍歷該物件時返回的順序一致,這個三個元素返回的值分別如下:

  •  Object.keys():返回包含物件鍵名的陣列;
  •  Object.values():返回包含物件鍵值的陣列;
  •  Object.entries():返回包含物件鍵名和鍵值的陣列。 
let obj = {   
  id: 1,   
  name: 'hello',   
  age: 18   
};  
console.log(Object.keys(obj));   // 輸出結果: ['id', 'name', 'age']  
console.log(Object.values(obj)); // 輸出結果: [1, 'hello', 18]  
console.log(Object.entries(obj));   // 輸出結果: [['id', 1], ['name', 'hello'], ['age', 18] 

注意

  •  Object.keys()方法返回的陣列中的值都是字串,也就是說不是字串的key值會轉化為字串。
  •  結果陣列中的屬性值都是物件本身可列舉的屬性,不包括繼承來的屬性。

3. Object.getOwnPropertyNames()

Object.getOwnPropertyNames()方法與Object.keys()類似,也是接受一個物件作為引數,返回一個數組,包含了該物件自身的所有屬性名。但它能返回不可列舉的屬性。

let a = ['Hello', 'World'];   
Object.keys(a) // ["0", "1"]  
Object.getOwnPropertyNames(a) // ["0", "1", "length"] 

這兩個方法都可以用來計算物件中屬性的個數:

var obj = { 0: "a", 1: "b", 2: "c"};  
Object.getOwnPropertyNames(obj) // ["0", "1", "2"]  
Object.keys(obj).length // 3  
Object.getOwnPropertyNames(obj).length // 3 

4. Object.getOwnPropertySymbols()

Object.getOwnPropertySymbols() 方法返回物件自身的 Symbol 屬性組成的陣列,不包括字串屬性:

let obj = {a: 1}  
// 給物件新增一個不可列舉的 Symbol 屬性  
Object.defineProperties(obj, {  
 [Symbol('baz')]: {  
  value: 'Symbol baz',  
  enumerable: false  
 }  
})  
// 給物件新增一個可列舉的 Symbol 屬性  
obj[Symbol('foo')] = 'Symbol foo'  
Object.getOwnPropertySymbols(obj).forEach((key) => {  
 console.log(obj[key])   
})  
// 輸出結果:Symbol baz Symbol foo 

5. Reflect.ownKeys()

Reflect.ownKeys() 返回一個數組,包含物件自身的所有屬性。它和Object.keys()類似,Object.keys()返回屬性key,但不包括不可列舉的屬性,而Reflect.ownKeys()會返回所有屬性key:

var obj = {  
 a: 1,  
 b: 2  
}  
Object.defineProperty(obj, 'method', {  
 value: function () {  
     alert("Non enumerable property")  
 },  
 enumerable: false  
})   
console.log(Object.keys(obj))  
// ["a", "b"]  
console.log(Reflect.ownKeys(obj))  
// ["a", "b", "method"] 

注意:

  •  Object.keys() :相當於返回物件屬性陣列;
  •  Reflect.ownKeys() :相當於 Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)。

總結:

物件方法 遍歷基本屬性 遍歷原型鏈 遍歷不可列舉屬性 遍歷Symbol
for in
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()

三、其他遍歷方法

1. for

for迴圈是應該是最常見的迴圈方式了,它由三個表示式組成,分別是宣告迴圈變數、判斷迴圈條件、更新迴圈變數。這三個表示式用分號分隔。可以使用臨時變數將陣列的長度快取起來,避免重複獲取陣列長度,當陣列較大時優化效果會比較明顯。

const arr = [1,2,3,4,5]  
for(let i = 0, len = arr.length; i < len; i++ ){  
  console.log(arr[i])  
} 

在執行的時候,會先判斷執行條件,再執行。for迴圈可以用來遍歷陣列,字串,類陣列,DOM節點等。可以改變原陣列。

2. while

while迴圈中的結束條件可以是各種型別,但是最終都會轉為布林值,轉換規則如下。

  •  Boolean:true為真,false為假;
  •  String:空字串為假,所有非空字串為真;
  •  Number:0為假,非0數字為真;
  •  null/Undefined/NaN:全為假;
  •  Object:全為真。 
let num = 1;        
while (num < 10){  
    console.log(num); 
    num ++;  
} 

while和for一樣,都是先判斷,再執行。只要指定條件為 true,迴圈就可以一直執行程式碼。

3. do / while

該方法會先執行再判斷,即使初始條件不成立,do/while迴圈也至少會執行一次。

let num = 10;           
do  
 {  
    console.log(num);  
    num--;  
  }  
while(num >= 0);            
console.log(num); //-1 

不建議使用do / while來遍歷陣列。

4. for await of

for await...of方法被稱為非同步迭代器,該方法是主要用來遍歷非同步物件。它是ES2018中引入的方法。

for await...of 語句會在非同步或者同步可迭代物件上建立一個迭代迴圈,包括 String,Array,類陣列,Map, Set和自定義的非同步或者同步可迭代物件。這個語句只能在 async function內使用:

function Gen (time) {  
  return new Promise((resolve,reject) => {  
    setTimeout(function () {  
       resolve(time)  
    },time)  
  })  
}  
async function test () {  
   let arr = [Gen(2000),Gen(100),Gen(3000)]  
   for await (let item of arr) {  
      console.log(Date.now(),item)  
   }  
}  
test() 

輸出結果:

鴻蒙官方戰略合作共建——HarmonyOS技術社群

【責任編輯:龐桂玉 TEL:(010)68476606】