JS中陣列常用的迴圈遍歷你會幾種
前言
陣列和物件作為一個最基礎資料結構,在各種程式設計語言中都充當著至關重要的角色,你很難想象沒有陣列和物件的程式語言會是什麼模樣,特別是 js ,弱型別語言,非常靈活。本文帶你瞭解常用陣列遍歷、物件遍歷的使用對比以及注意事項。
陣列遍歷
隨著 JS 的不斷髮展,截至 ES7 規範已經有十多種遍歷方法。下面按照功能類似的方法為一組,來介紹陣列的常用遍歷方法。
for、forEach、for ...of
const list = [1,2,3,4,5,6,7,8,10,11]; for (let i = 0,len = list.length; i < len; i++) { if (list[i] === 5) { break; // 1 2 3 4 // continue; // 1 2 3 4 6 7 8 undefined 10 11 } console.log(list[i]); } for (const item of list) { if (item === 5) { break; // 1 2 3 4 // continue; // 1 2 3 4 6 7 8 unwww.cppcns.comdefined 10 11 } console.log(item); } list.forEach((item,index,arr) => { if (item === 5) return; console.log(index); // 0 1 2 3 5 6 7 9 10 console.log(item); // 1 2 3 4 6 7 8 9 10 });
小結
- 三者都是基本的由左到右遍歷陣列。
- forEach 無法跳出迴圈,for 和 for ..of 可以使用 break 或者 continue 跳過或中斷。
- for ...of 直接訪問的是實際元素,for 遍歷陣列索引,forEach 回撥函式引數更豐富,元素、索引、原陣列都可以獲取。
- for ...of 與 for 如果陣列中存在空元素,同樣會執行。
some、every
const list = [ { name: '頭部導航',backward: false },{ name: '輪播',backward: true },{ name: '頁尾',]; const someBackward = list.some(item => item.backward); // someBackward: true const everyNewest = list.every(item => !item.backward); // everyNewest: false
小結
- 二者都是用來做陣列條件判斷的,都是返回一個布林值。
- 二者都可以被中斷。
- some 若某一元素滿足條件,返回 true,迴圈中斷。所有元素不滿足條件,返回 false。
- every 與 some 相反,若有一元素不滿足條件,返回 false,迴圈中斷。所有元素滿足條件,返回 true。
filter、map
const list = [ { name: '頭部導航',type: 'nav',id: 1 },type: 'content',id: 2 },id: 3 },]; const resultList = list.filter(item => { console.log(item); return item.type === 'nav'; }); // resultList: [ // { name: '頭部導航',// { name: '頁尾',// ] const newList = list.map(item => { console.log(item); return item.id; }); // newList: [1,empty,3] // list: [ // { name: '頭部導航',// empty,// { name: '輪播',// ]
小結
- 二者都是生成一個新陣列,都不會改變原陣列(不包括遍歷物件陣列時,在回撥函式中操作元素物件)。
- 二者都會跳過空元素。有興趣的同學可以自己列印一下。
- map 會將回調函式的返回值組成一個新陣列,陣列長度與原陣列一致。
- filter 會將符合回撥函式條件的元素組成一個新陣列。
- map 生成的新陣列元素是可自定義。
- filter 生成的新陣列元素不可自定義,與對應原陣列元素一致。
find、findIndex
const list = [ { name: '頭部導航',]; const result = list.find((item) => item.id === 3); // result: { name: '頁尾',id: 3 } result.name = '底部導航'; // list: [ // { name: '頭部導航',// { name: '底部導航',// ] const index = list.findIndex((item) => item.id === 3); // index: 2 list[index].name // '底部導航';
小結
- 二者都是用來查詢陣列元素。
- find 方法返回陣列中滿足 callback 函式的第一個元素的值。如果不存在返回 undefined。
- findIndex 它返回陣列中找到的元素的索引,而不是其值,如果不存在返回 -1。
reduce、reduceRight
reduce 方法接收兩個引數,第一個引數是回撥函式(callback) ,第二個引數是初始值(initialValue)。
reduceRight 方法除了與 reduce 執行方向相反外(從右往左),其他完全與其一致。
回撥函式接收四個引數:
- accumulator:MDN 上解釋為累計器,但我覺得不恰當,按我的理解它應該是截至當前元素,之前所有的陣列元素被回撥函式處理累計的結果。
- current:當前被執行的陣列元素。
- currentIndex: 當前被執行的陣列元素索引。
- sourceArray:原陣列,也就是呼叫 reduce 方法的陣列。
如果不傳入初始值,reduce 方法會從索引 1 開始執行回撥函式,如果傳入初始值,將從索引 0 開始、並從初始值的基礎上累計執行回撥。
計算物件陣列某一屬性的總和
const list = [ { name: 'left',width: 20 },{ name: 'center',width: 70 },{ name: 'right',width: 10 },]; const total = list.reduce((currentTotal,item) => { return currentTotal 程式設計客棧+ item.width; },0); // total: 100
物件陣列的去重,並統計每一項重複次數
const list = [ { name: 'left',{ name: 'left',]; const repeatTime = {}; const result = list.reduce((array,item) => { if (r程式設計客棧epeatTime[item.name]) { repeatTime[item.name]++; return array; } repeatTime[item.name] = 1; return [...array,item]; },[]); // repeatTime: { left: 2,right: 3,center: 1 } // result: [ // { name: 'left',// { name: 'right',// { name: 'center',// ]
物件陣列最大/最小值獲取
const list = [ { name: 'left',width: 30 },{ name: 'top',width: 40 },{ name: 'bottom',]; const max = list.reduce((curItem,item) => { return curItem.width >= item.width ? curItem : item; }); const min = list.reduce((curItem,item) => { return curItem.width <= item.width ? curItem : item; }); // max: { name: "center",width: 70 } // min: { name: "left",width: 20 }
reduce 很強大,更多奇技淫巧推薦檢視這篇《25個你不得不知道的陣列reduce高階用法》
效能對比
說了這麼多,那這些遍歷方法, 在效能上有什麼差異呢?我們在 Chrome 瀏覽器中嘗試。我採用每個迴圈執行10次,去除最大、最小值 取平均數,降低誤差。
var list = Array(100000).fill(1) console.time('for'); for (let index = 0,len = list.length; index < len; index++) { } console.timeEnd('for'); // for: 2.427642822265625 ms console.time('every'); list.every(() => { return true }) console.timeEnd('every') // some: 2.751708984375 ms console.time('some'); list.some(() => { return false }) console.timeEnd('some') // some: 2.786590576171875 ms console.time('foreach'); list.forEach(() => {}) console.timeEnd('foreach'); // foreach: 3.126708984375 ms console.time('map'); list.map(() => {}) console.timeEnd('map'); // map: 3.743743896484375 ms console.time('forof'); for (let index of list) { } console.timeEnd('forof') // forof: 6.33380126953125 ms
從列印結果可以看出,for 迴圈的速度最快,for ...of 迴圈最慢。
常用遍歷的終止、效能表格對比
最後,不同瀏覽器核心 也會有些差異,有興趣的同學也可以嘗試一下。
物件遍歷
在物件遍歷中,經常需要遍歷物件的鍵、值,ES5 提供了 for...in 用來遍歷物件,然而其涉及物件屬性的“可列舉屬性”、原型鏈屬性等,下面將從 程式設計客棧Object 物件本質探尋各種遍歷物件的方法,並區分常用方法的一些特點。
for in
Object.prototype.fun = () => {}; const obj = { 2: 'a',1: 'b' }; for (const i in obj) { console.log(i,':',obj[i]); } // 1: b // 2: a // fun : () => {} Object 原型鏈上擴充套件的方法也被遍歷出來 for (const i in obj) { if (Object.prototype.hasOwnProperty.call(obj,i)) { console.log(i,obj[i]); } } // name : a 不屬於自身的屬性將被 hasOwnProperty 過濾
小結
使用 for in 迴圈時,返回的是所有能夠通過物件訪問的、可列舉的屬性,既包括存在於例項中的屬性,也包括存在於原型中的例項。如果只需要獲取物件的例項屬性,可以使用 hasOwnProperty 進行過濾。
使用時,要使用(const x in a)而不是(x in a)後者將會建立一個全域性變數。
for in 的迴圈順序,參考【javascript 權威指南】(第七版)6.6.1。
- 先列出名字為非負整數的字串屬性,按照數值順序從最小到最大。這條規則意味著陣列和類陣列物件的屬性會按照順序被列舉。
- 在列出類陣列索引的所有屬性之後,在列出所有剩下的字串名字(包括看起來像整負數或浮點數的名字)的屬性。這些屬性按照它們新增到物件的先後順序列出。對於在物件字面量中定義的屬性,按照他們在字面量中出現的順序列出。
- 最後,名字為符號物件的屬性按照它們新增到物件的先後順序列出。
Object.keys
Object.prototype.fun = () => {}; const str = 'ab'; console.log(Object.keys(str)); // ['0','1'] const arr = ['a','b']; console.log(Object.keys(arr)); // ['0','1'] const obj = { 1: 'b',0: 'a' }; console.log(Object.keys(obj)); // ['0','1']
小結
用於獲取物件自身所有的可列舉的屬性值,但不包括原型中的屬性,然後返回一個由屬性名組成的陣列。
Object.values
Ob程式設計客棧ject.prototype.fun = () => {};
const str = 'ab';
console.log(Object.values(str));
// ['a','b']
const arr = ['a','b'];
console.log(Object.values(arr));
// ['a','b']
const obj = { 1: 'b',0: 'a' };
console.log(Object.values(obj));
// ['a','b']
小結
用於獲取物件自身所有的可列舉的屬性值,但不包括原型中的屬性,然後返回一個由屬性值組成的陣列。
Object.entries
const str = 'ab'; for (const [key,value] of Object.entries(str)) { console.log(`${key}: ${value}`); } // 0: a // 1: b const arr = ['a','b']; for (const [key,value] of Object.entries(arr)) { console.log(`${key}: ${value}`); } // 0: a // 1: b const obj = { 1: 'b',0: 'a' }; for (const [key,value] of Object.entries(obj)) { console.log(`${key}: ${value}`); } // 0: a // 1: b
小結
用於獲取物件自身所有的可列舉的屬性值,但不包括原型中的屬性,然後返回二維陣列。每一個子陣列由物件的屬性名、屬性值組成。可以同時拿到屬性名與屬性值的方法。
Object.getOwnPropertyNames
Object.prototype.fun = () => {}; Array.prototype.fun = () => {}; const str = 'ab'; console.log(Object.getOwnPropertyNames(str)); // ['0','1','length'] const arr = ['a','b']; console.log(Object.getOwnPropertyNames(arr)); // ['0','length'] const obj = { 1: 'b',0: 'a' }; console.log(Object.getOwnPropertyNames(obj)); // ['0','1']
小結
用於獲取物件自身所有的可列舉的屬性值,但不包括原型中的屬性,然後返回一個由屬性名組成的陣列。
總結
我們對比了多種常用遍歷的方法的差異,在瞭解了這些之後,我們在使用的時候需要好好思考一下,就能知道那個方法是最合適的。歡迎大家糾正補充。
到此這篇關於JS中陣列常用的迴圈遍歷的文章就介紹到這了,更多相關JS陣列迴圈遍歷內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!