JavaScript中數組中遍歷的方法


前言

最近看了好幾篇總結數組中遍歷方法的文章,然而“紙上得來終覺淺”,決定此事自己干。於是小小總結,算是自己練手了。

各種數組遍歷方法

數組中常用的遍歷方法有四種,分別是:

  • for
  • for-in
  • forEach
  • for-of (ES6)

for

使用for循環來遍歷一個數組,代碼如下:

// for循環
    let arr=[2,4,6,10];
    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }

當數組的長度不回改變時,我們使用一個變量來存儲數組的長度arr.length,以獲得更好的效率(因為每次比較的時候都省去計算arr.length)。改進后的算法如下:

 // for循環
    let arr=[2,4,6,10];
    for(let i=0,len=arr.length;i<len;i++){
        console.log(arr[i]);
    }

當然如果不考慮輸出順序,為了簡化,也可以使用i--,代碼如下:

 // for循環
	let arr=[2,4,6,10];
    for(let i=arr.length;i>=0;i--){
        console.log(arr[i]);
    }

for-in

通常情況下,我們可以這樣使用:

 //for-in 循環
    let arr=[2,4,6,10];
    let index;
    for (index in arr){
        console.log(arr[index]);
    }

輸出結果

	arr[0]=2
    arr[1]=4
    arr[2]=6
    arr[3]=10

來,我們再看一個例子:

	//for-in 循環
    let per={
        name:"zhang San",
        sex:'male',
        age:18
    };
    for(let key in per){
        console.log("per[" + key + "]=" + per[key]);
    }

輸出結果

	per[name]=zhang San
	per[sex]=male
	per[age]=18

根據上面的兩個例子可以看到,for-in不僅可以遍歷數組還可以遍歷對象
注意:文章深入了解 JavaScript 中的 for 循環 指出for-in 遍歷屬性的順序並不確定,即輸出的結果順序與屬性在對象中的順序無關,也與屬性的字母順序無關,與其他任何順序也無關。,但是我在實際操作中,發現輸出一直是上面的結果,即其按照了定義屬性的順序輸出了。

此時,我們回頭再次看一個for-in的例子:

	Array.prototype.fatherName = "Father";
	let arr=[2,4,6,10];
    arr.name="hello,world!";
    let index;
    for (index in arr){
        console.log('arr['+index+']='+arr[index]);
    }

輸出結果

	arr[0]=2
    arr[1]=4
    arr[2]=6
    arr[3]=10
    arr[name]=hello,world!
    arr[Father]=Father

我們發現一個問題,使用for-in在遍歷的時候,它不僅遍歷了對象上的屬性,而且還遍歷了對象父類原型上的屬性。
所以for-in並不適合遍歷Array中的元素,更適合遍歷對象中的屬性,這也是創造for-in的初衷。但是對於稀疏數組,使用它卻是極好的。例子如下:

let key;
let arr = [];
arr[0] = "a";
arr[999] = "b";
arr[99999] = "c";
for(key in arr) {
	if(arr.hasOwnProperty(key)  && /^0$|^[1-9]\d*$/.test(key) && key <= 100000) {
        console.log(arr[key]);
    }
}

在上面的例子當中,由於for-in只會遍歷存在的實體,因此使用for-in循環,只需要遍歷3次,而使用for循環則需要遍歷100000次。

for-in的性能

由於使用它的時候,每次需要遍歷對象中存在的實體,以及對應的原型鏈上的屬性。因此其速度相比較其他for循環,要慢一些。所以除非明確要迭代一個屬性數量未知的對象,否則應該避免使用for-in

forEach

forEach()方法為數組中的每一個有效元素執行一次callback函數。遍歷數組讓數組中的每一個元素做一件事情。那些已經被刪除(使用delete方法等情況)或者未初始化的項將被跳過(但不包含那些值未undefined的項目)。
注意,callback函數將依次被傳入3個參數:

  • 數組當前項的值
  • 數組當前項的索引
  • 數組對象本身

看個例子:

	let arr = [];
    arr[0] = "a";
    arr[3] = "b";
    arr[10] = "c";
    arr[name] = "Hello world";
    arr.forEach((data, index, array) => {
        console.log(data, index, array);
    });

輸出結果

a 0 ["a", 3: "b", 10: "c", name: "Hello world"]
b 3 ["a", 3: "b", 10: "c", name: "Hello world"]
c 10 ["a", 3: "b", 10: "c", name: "Hello world"]

值得注意的是:

  • 沒有輸出name。why? 因為name是arr的屬性,而0,3,10都是arr的索引。
  • 這里的 index 是 Number 類型,並且也不會像 for-in 一樣遍歷原型鏈上的屬性。

forEach的性能

在不同瀏覽器下測試的結果都是 forEach 的速度不如 for。可以看到forEach主要應用在遍歷數組,但是它的性能並不如for,因此可以使用for就盡量不要使用forEach。

for-of(ES6中新增)

for-of是ES6中新增的一個遍歷數組或者類數組的方法。它的出現主要是為了解決ES5中3種遍歷方式的缺陷:

  • forEach 不能break 或者return
  • for-in 的缺點更加明顯。它不僅遍歷了數組中的元素,還遍歷了自定義屬性,甚至連原型鏈上的屬性都被訪問到。

因此使用for-of的優勢在於:

  • 這是最簡潔、直接遍歷數組的方式
  • 這個方法避開了for-in循環的缺陷
  • 與forEach不同,它可以正確響應break,continue,return 語句。

說了這么多for-of的優點,那么它有沒有缺點呢?

for-of不支持普通對象遍歷,如果想要遍歷普通對象,使用for-in

例子:

	let arr=[3,7,9];
    for (let key of arr){
        console.log(key);
    }

結果

3
7
9

性能

由於for-of也是只遍歷可迭代對象的數據,相比於for-in,效率會更高。

Other 循環方法

說明:下面的方法,Map,Reduce,Filter,Every,some方法都是面向數組的。不是普通對象。

map(不改變原數組)

map方法(映射)給數組中的每個元素執行一次callback函數,執行callback函數的返回值組成一個新數組。未被初始化項、已經被刪除項(使用delete等方法)、數組的屬性不會被執行callback函數。
請看如下例子:

	let arr=new Array(5);
    arr[1]=1;
    arr[2]=2;
    arr.name="hello,world!";
    delete(arr[2]);

    let fireArr=arr.map(item=>item*5);
    console.log(fireArr);

輸出結果:

[empty,5,empty,empty,empty]

reduce

reduce方法,讓數組的前項后項進行某種計算。累計最終的結果。
例子:

	//reduce方法
    let arr = [4,7,8,3];
    let total= arr.reduce( function (x, y) {
        console.log(x);
        return x + y;

    });
    console.log(total);

輸出結果

   4 
   11
   19
   22

filter(不改變原數組)

filter的意思是濾鏡、過濾器,顧名思義就是用來篩選符合某種條件的元素,將符合條件的元素重新組成一個新的數組。
還是看一個例子:

	let arr = [2,3,4,5,6];
    arr.name=8;
    let morearr = arr.filter(function (number) {
        return number > 3
    });
    console.log(morearr);

輸出結果

    4, 5, 6

在上面的例子中,將大於索引大於3的元素輸出。而不會輸出屬性。

every

every方法為數組中的每個元素執行一次 callback 函數,當它找到一個使 callback 返回 false(表示可轉換為布爾值 false 的值)的元素,就立刻返回false。否則,callback為每個元素返回true,every就返回true。
看一面的例子

	let arr = [1,2,3,4,5];
    let result = arr.every(function (item, index) {
        return item > 0
    });
    console.log(result);

返回結果

	true

some

some 為數組中的每一個元素執行一次 callback 函數,直到找到一個使得 callback 返回一個“真值”(即可轉換為布爾值 true 的值)。如果找到了這樣一個值,some 將會立即返回 true。否則,some 返回 false。

	let arr = [1,2,3,4,5];
    let result = arr.some(function (item,index) {
        return item > 3
    });
    console.log(result);

iterator

最后,還有一個東西不得不介紹,ES6新增的iterator,迭代器。
看例子:

const arr = ['a', 'b', 'c'];
const iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

iterator名叫“迭代器”,iter.next()實際上是探測下一個位置是否有數據的探測器,如果有就返回false;如果下一個位置上沒有,就返回true。

效率問題

1、循環效率

	for > for-of > forEach > filter > map > for-in

可以看到 的是for循環的速度是最快的,是最老的循環方法,也是優化得最好的;其次是for-of這個是es6才新增的循環非常好用,最慢是for-in,原因是它遍歷了原型鏈上的屬性。

2、some 和 every

他們都是根據判斷條件,返回給整個數組Boolean 值的方法,every 的執行速度會比some快很多。

  • every是不滿足判斷條件后,立即返回“假的”false值給整個數組。否則繼續執行,返回true。
  • some是滿足判斷條件后,立刻返回“真值”true給整個數組。否則繼續執行,返回false。

參考文檔:
JavaScript 數組遍歷方法的對比
深入了解 JavaScript 中的 for 循環


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM