網站搜尋

在 JavaScript 中遍歷陣列


在 Java 中,你可以使用 for 迴圈來遍歷陣列中的物件,如下所示:

String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray) {
    // Do something
}

我可以在 JavaScript 中做同樣的事情嗎?

答案#1

三個主要選項:

  1. for (var i = 0; i < xs.length; i++) { console.log(xs[i]); }
  2. xs.forEach((x, i) => console.log(x));
  3. for (const x of xs) { console.log(x); }

詳細的範例如下。


1. 連續的 for 迴圈:

var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
    console.log(myStringArray[i]);
    //Do something
}

優點

  • 適用於所有環境
  • 你可以使用 break 和 continue 流程控制語句

缺點

  • 過於冗長
  • 命令式
  • 容易出現一個偏差錯誤(有時也稱為柵欄柱錯誤)

2. Array.prototype.forEach:

ES5 規範引入了許多有益的陣列方法。其中一個,Array.prototype.forEach,為我們提供了一種簡潔的方式來遍歷陣列:

const array = ["one", "two", "three"]
array.forEach(function (item, index) {
  console.log(item, index);
});

自從 ES5 規範發佈(2009 年 12 月)到撰寫本文時已經將近十年,它已被幾乎所有現代桌面、伺服器和行動環境的引擎實作,因此使用它們是安全的。

使用 ES6 箭頭函數語法,會更簡潔:

array.forEach(item => console.log(item));

箭頭函數也被廣泛實作,除非你打算支援古老平台(例如 Internet Explorer 11);否則你也可以安全使用。

優點

  • 非常短且簡潔。
  • 陳述式

缺點

  • 不能使用 break/continue

通常,你可以通過在迭代陣列元素之前先篩選它們來替代在命令式迴圈中使用 break,例如:

array.filter(item => item.condition < 10)
     .forEach(item => console.log(item))

請記住,如果你正在迭代一個數組 以從中構建另一個數組,你應該使用 map。我已經多次看到這種反模式。

反模式:

const numbers = [1,2,3,4,5], doubled = [];

numbers.forEach((n, i) => { doubled[i] = n * 2 });

map 的正確使用情境:

const numbers = [1,2,3,4,5];
const doubled = numbers.map(n => n * 2);

console.log(doubled);

另外,如果你試圖將陣列縮減為一個值,例如,你想對一個數字陣列求和,你應該使用reduce方法。

反模式:

const numbers = [1,2,3,4,5];
const sum = 0;
numbers.forEach(num => { sum += num });

reduce 的正確使用方式:

const numbers = [1,2,3,4,5];
const sum = numbers.reduce((total, n) => total + n, 0);

console.log(sum);

3. ES6 for-of 語句:

ES6 標準引入了可迭代對象的概念,並定義了一種用於遍歷數據的新結構,即 for...of 語句。

這個語句適用於任何類型的可迭代對象,也適用於生成器(任何具有 \[Symbol.iterator\] 屬性的對象)。

陣列物件依定義在 ES6 中是內建的可迭代物件,因此你可以對它們使用這個語句:

let colors = ['red', 'green', 'blue'];
for (const color of colors){
    console.log(color);
}

優點

  • 它可以遍歷各種各樣的對象。
  • 可以使用一般的流程控制語句(break/continue)。
  • 用於逐一迭代非同步值。

缺點

  • 如果你針對的是較舊的瀏覽器,轉譯後的輸出可能會讓你感到驚訝。

不要使用 for...in

@zipcodeman 建議使用 for...in 語句,但在遍歷陣列時應避免使用 for-in,該語句是用來 列舉 物件屬性的。

不應該用於類陣列物件,因為:

  • 迭代的順序無法保證;陣列索引可能不會以數字順序被訪問。
  • 繼承的屬性也會被列舉。

第二點是,它可能會給你帶來很多問題,例如,如果你擴展 Array.prototype 對象以包含那裡的一個方法,該屬性也將被列舉出來。

例如:

Array.prototype.foo = "foo!";
var array = ['a', 'b', 'c'];

for (var i in array) {
    console.log(array[i]);
}

上述程式碼將在控制台輸出「a」、「b」、「c」和「foo!」.

如果你使用某些高度依賴原生原型擴展的庫(例如 MooTools),這可能會特別成為一個問題。

如我之前所說,for-in 敘述是用來列舉物件的屬性,例如:

var obj = {
    "a": 1,
    "b": 2,
    "c": 3
};

for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) {
        // or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
        console.log("prop: " + prop + " value: " + obj[prop])
    }
}

在上面的例子中,hasOwnProperty 方法允許你僅列舉 自身屬性。就是這樣,只有物件實際擁有的屬性,沒有繼承的屬性。

我建議你閱讀以下文章:

  • 列舉 VS 迭代

答案#2

是的,假設你的實作包括在 ECMAScript 2015(「和諧」版本)中引入的 for…of 功能…如今這是一個相當安全的假設。

它的運作方式如下:

// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
  // ... do something with s ...
}

或者更好的是,既然 ECMAScript 2015 也提供了區塊作用域變數:

// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
  // ... do something with s ...
}
// s is no longer defined here

(變數 s 在每次迭代中都是不同的,但只要在循環體內沒有修改它,仍然可以聲明為 const。)

關於稀疏陣列的說明:在 JavaScript 中,陣列並不一定實際存儲與其 length 屬性報告的數量相同的項目;該數字僅僅比存儲值的最高索引大一。如果陣列存儲的元素比其長度所示的少,則稱該陣列為 稀疏。例如,只在索引 3、12 和 247 處有項目的陣列是完全合法的;這樣的陣列 length 為 248,但實際只存儲 3 個值。如果你試圖訪問任何其他索引處的項目,陣列看起來會顯示該處的 undefined 值,但該陣列仍然不同於實際存儲了 undefined 值的陣列。你可以通過多種方式看出這種區別,例如在 Node REPL 顯示陣列的方式中:

> a              // array with only one item, at index 12
[ <12 empty items>, 1 ]
> a[0]           // appears to have undefined at index 0
undefined
> a[0]=undefined // but if we put an actual undefined there
undefined
> a              // it now looks like this
[ undefined, <11 empty items>, 1 ]

所以當你想要「迴圈遍歷」一個陣列時,你需要回答一個問題:你是想要遍歷陣列長度所表示的整個範圍,並對任何缺失的元素處理 undefined,還是只想處理實際存在的元素?這兩種方法都有很多應用;這完全取決於你使用這個陣列的目的。

如果你使用 for..of 迭代一個陣列,迴圈的主體會被執行 length 次,對於陣列中實際不存在的任何項目,迴圈控制變數會被設為 undefined。根據你的「對……做些事情」程式碼的細節,這種行為可能是你想要的,但如果不是,你應該使用不同的方法。

當然,一些開發者無論如何也別無選擇,只能使用不同的方法,因為無論出於何種原因,他們的目標是尚不支援 for...of 的 JavaScript 版本。

只要您的 JavaScript 實作符合 ECMAScript 規範的前一版(例如排除了 9 以前版本的 Internet Explorer),那麼您就可以使用 Array#forEach 迭代方法來取代迴圈。在那種情況下,您需要傳遞一個函數,該函數將在陣列的每個項目上被調用:

var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) { 
     // ... do something with s ...
} );

如果您的實現支持 ES6+,您當然可以使用箭頭函數:

myStringArray.forEach( s => { 
     // ... do something with s ...
} );

與 for...of 不同,.forEach 只會對陣列中實際存在的元素呼叫函數。如果傳入我們假設的包含三個元素且長度為 248 的陣列,它只會呼叫函數三次,而不是 248 次。如果這就是你想要處理稀疏陣列的方式,即使你的解釋器支援 for...of,.forEach 也可能是可行的做法。

最後一個選項,在 所有 版本的 JavaScript 中都有效,是明確的計數迴圈。你只需從 0 計數到長度減 1,並將計數器用作索引。基本的迴圈如下所示:

var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
  s = myStringArray[i];
  // ... do something with s ...
}

這種方法的一個優點是你可以選擇如何處理稀疏陣列。上述程式碼將會執行迴圈主體完整的 length 次,對於任何缺失的元素,s 會被設為 undefined,就像 for..of 一樣;如果你希望只處理稀疏陣列中實際存在的元素,就像 .forEach 一樣,你可以在索引上添加一個簡單的 in 測試:

var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
  if (i in myStringArray) {
    s = myStringArray[i];
    // ... do something with s ...
  }
}

根據你實作的優化方式,將長度值分配給區域變數(與在迴圈條件中包含完整的 myStringArray.length 表達式相比)可以顯著提升效能,因為這樣可以跳過每次迴圈時的屬性查找。你可能會在迴圈初始化子句中看到長度緩存的用法,如下所示:

var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {

明確的計數迴圈也意味著,如果你想要的話,你可以訪問每個值的索引。索引也會作為額外的參數傳遞給你傳給 forEach 的函數,因此你也可以那樣訪問它:

myStringArray.forEach( (s,i) => {
   // ... do something with s and i ...
});

for...of 不會給你每個對象對應的索引,但只要你迭代的對象實際上是 Array 的實例(而不是 for..of 可以操作的其他可迭代類型之一),你可以使用 Array#entries 方法將它轉換為 [索引, 項目] 對的數組,然後對其進行迭代:

for (const [i, s] of myStringArray.entries()) {
  // ... do something with s and i ...
}

其他人提到的 for...in 語法是用來遍歷物件的屬性;由於在 JavaScript 中,陣列只是具有數字屬性名稱(以及自動更新的 length 屬性)的物件,因此理論上你可以用它來遍歷陣列。但問題在於,它不會只限制於數字屬性值(記住,即使是方法實際上也是值為閉包的屬性),也不保證按數字順序迭代。因此,for...in 語法 不應該 用於遍歷陣列。

答案#3

你可以使用 map,這是一種函數式編程技巧,在其他語言如 Python 和 Haskell 中也可用。

[1,2,3,4].map( function(item) {
     alert(item);
})

一般語法為:

array.map(func)

一般來說,func 會接受一個參數,這個參數是陣列中的一個項目。但在 JavaScript 的情況下,它可以接受第二個參數,這是該項目的索引,還有第三個參數,這本身就是陣列。

array.map 的返回值是另一個陣列,所以你可以這樣使用它:

var x = [1,2,3,4].map( function(item) {return item * 10;});

現在 x 是 [10,20,30,40]。

你不必將函數內聯寫。它可以是一個獨立的函數。

var item_processor = function(item) {
      // Do something complicated to an item
}

new_list = my_list.map(item_processor);

這大致上相當於:

 for (item in my_list) {item_processor(item);}

除非你沒有得到 new_list。

答案#4

對於 myStringArray 中的每個 s {

(直接回答你的問題:現在你可以了!)

大多數其他答案都是正確的,但它們沒有提到(截至本文撰寫時)ECMAScript 6 2015 引入了一種新的迭代機制,即 for..of 迴圈。

這種新語法是在 JavaScript 中迭代陣列的最優雅方式(只要你不需要迭代索引)。

它目前可與 Firefox 13+、Chrome 37+ 一起使用,並且不原生支援其他瀏覽器(請參閱下方的瀏覽器相容性)。幸運的是,我們有 JavaScript 編譯器(例如 Babel),讓我們今天就能使用下一代功能。

它也適用於 Node.js(我在版本 0.12.0 上測試過)。

迭代陣列

// You could also use "let" or "const" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) {
   console.log(letter);
}

迭代物件陣列

const band = [
  {firstName : 'John', lastName: 'Lennon'},
  {firstName : 'Paul', lastName: 'McCartney'}
];

for(const member of band){
  console.log(member.firstName + ' ' + member.lastName);
}

迭代生成器:

(範例摘自 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)

function* fibonacci() { // A generator function
  let [prev, curr] = [1, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (const n of fibonacci()) {
  console.log(n);
  // Truncate the sequence at 1000
  if (n >= 1000) {
    break;
  }
}

相容性表格: http://kangax.github.io/compat-table/es6/#test-for..of_loops

規格: http://wiki.ecmascript.org/doku.php?id=harmony:iterators

}

答案#5

在 JavaScript 中,不建議使用 for-in 迴圈來遍歷 Array,但最好使用像這樣的 for 迴圈:

for(var i=0, len=myArray.length; i < len; i++){}

它也經過優化(「快取」了陣列長度)。如果你想了解更多,請閱讀我關於這個主題的文章。

答案#6

6 種不同的方法來遍歷陣列

你可以透過很多不同的方法來遍歷一個陣列。我已經將我最喜歡的六種方法從上到下排序好了。

1. 使用 JavaScript 的 for 迴圈

當只是簡單地遍歷一個陣列時,for 迴圈是我的首選。

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

2. 使用 forEach 迴圈

forEach 迴圈是一種遍歷陣列的現代方法。此外,它對陣列和元素提供了更多的靈活性和控制。

let array = [1, 2, 3, 4, 5];
array.forEach((element) => {
  console.log(element);
});

3. 使用 for...of

for...of 迴圈讓你可以直接存取陣列元素。

let array = [1, 2, 3, 4, 5];
for (let element of array) {
  console.log(element);
}

4. 使用 for...in 迴圈

`for...in` 給你一個鍵,透過它你可以存取陣列元素。

let array = [1, 2, 3, 4, 5];
for(let index in array){
  console.log(array[index]);
}

5. 使用 while 迴圈

while 迴圈也可以用來遍歷陣列。

let array = [1, 2, 3, 4, 5];
let length = array.length;
while(length > 0){
  console.log(array[array.length - length]);
  length--;
}

6. 使用 do...while 迴圈

同樣地,我使用 do...while 迴圈

let array = [1, 2, 3, 4, 5];
let length = array.length;
do {
  console.log(array[array.length - length]);
  length--;
}
while (length > 0)

答案#7

Opera、Safari、Firefox 和 Chrome 現在都共享一組增強的 Array 方法,用於優化許多常見的循環。

你可能不需要全部,但它們可能非常有用,或者如果每個瀏覽器都支持它們的話就會非常有用。

Mozilla 實驗室公佈了他們和 WebKit 都使用的演算法,這樣你就可以自己添加它們。

filter 返回一個滿足某些條件或測試的項目陣列。

every 如果每個陣列成員都通過測試,則返回 true。

some 如果有任何一個通過測試則返回 true。

forEach 對每個陣列成員執行一個函式,並且不返回任何值。

map 像 forEach,但它返回每個元素操作結果的陣列。

這些方法的第一個參數都是一個函數,並且有一個可選的第二個參數,該參數是一個物件,你希望將其作用範圍施加於陣列成員,當它們通過函數迴圈時。

先忽略它,直到你需要它為止。

indexOf 和 lastIndexOf 找到與其參數完全匹配的第一個或最後一個元素的適當位置。

(function(){
    var p, ap= Array.prototype, p2={
        filter: function(fun, scope){
            var L= this.length, A= [], i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        val= this[i];
                        if(fun.call(scope, val, i, this)){
                            A[A.length]= val;
                        }
                    }
                    ++i;
                }
            }
            return A;
        },
        every: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && !fun.call(scope, this[i], i, this))
                        return false;
                    ++i;
                }
                return true;
            }
            return null;
        },
        forEach: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
            }
            return this;
        },
        indexOf: function(what, i){
            i= i || 0;
            var L= this.length;
            while(i< L){
                if(this[i]=== what)
                    return i;
                ++i;
            }
            return -1;
        },
        lastIndexOf: function(what, i){
            var L= this.length;
            i= i || L-1;
            if(isNaN(i) || i>= L)
                i= L-1;
            else
                if(i< 0) i += L;
            while(i> -1){
                if(this[i]=== what)
                    return i;
                --i;
            }
            return -1;
        },
        map: function(fun, scope){
            var L= this.length, A= Array(this.length), i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        A[i]= fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
                return A;
            }
        },
        some: function(fun, scope){
            var i= 0, L= this.length;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && fun.call(scope, this[i], i, this))
                        return true;
                    ++i;
                }
                return false;
            }
        }
    }
    for(p in p2){
        if(!ap[p])
            ap[p]= p2[p];
    }
    return true;
})();

答案#8

介紹

自從我在大學時期,我就使用 Java、JavaScript、Pascal、ABAP、PHP、Progress 4GL、C/C++ 以及可能還有一些我現在想不起來的其他語言進行程式設計。

雖然它們各自都有自己的語言特性,但這些語言都共享許多相同的基本概念。這些概念包括程序/函數、IF語句、FOR迴圈和WHILE迴圈。


傳統的 for 迴圈

傳統的 for 迴圈有三個組成部分:

  1. 初始化: 在第一次執行 look 區塊之前執行
  2. 條件:在每次執行迴圈區塊之前檢查條件,如果為假則退出迴圈
  3. 事後想法: 每次在迴圈區塊執行後執行

這三個組件彼此之間以 ; 符號分隔。每個組件的內容都是可選的,這意味著以下是可能的最簡單的 for 迴圈:

for (;;) {
    // Do stuff
}

當然,你需要在那個 for 迴圈中某處加入 if(condition === true) { break; } 或 if(condition === true) { return; } 才能讓它停止運行。

通常,初始化用於宣告索引,條件用於將該索引與最小值或最大值比較,而後置語句用於增加索引:

for (var i = 0, length = 10; i < length; i++) {
    console.log(i);
}

使用傳統的 for 迴圈來遍歷陣列

傳統遍歷陣列的方法是這樣的:

for (var i = 0, length = myArray.length; i < length; i++) {
    console.log(myArray[i]);
}

或者,如果你想要倒著循環,你可以這樣做:

for (var i = myArray.length - 1; i > -1; i--) {
    console.log(myArray[i]);
}

然而,仍有許多可能的變化,例如這一種:

for (var key = 0, value = myArray[key], length = myArray.length; key < length; value = myArray[++key]) {
    console.log(value);
}

...或者這個...

var i = 0, length = myArray.length;
for (; i < length;) {
    console.log(myArray[i]);
    i++;
}

...或者這個:

var key = 0, value;
for (; value = myArray[key++];){
    console.log(value);
}

哪一種效果最好,很大程度上取決於個人的喜好以及你所實施的具體使用情況。

請注意,所有這些變體都被所有瀏覽器支持,包括非常非常舊的瀏覽器!


一個 while 迴圈

一個 for 迴圈的替代方法是 while 迴圈。要循環遍歷一個陣列,你可以這麼做:

var key = 0;
while(value = myArray[key++]){
    console.log(value);
}

像傳統的 for 迴圈一樣,即使是最舊的瀏覽器也支援 while 迴圈。

另外,請注意,每個 while 迴圈都可以重寫為 for 迴圈。例如,上面這個 while 迴圈的行為與這個 for 迴圈完全相同:

for(var key = 0; value = myArray[key++];){
    console.log(value);
}

For...in 與 for...of

在 JavaScript 中,你也可以這樣做:

for (i in myArray) {
    console.log(myArray[i]);
}

然而,這應該謹慎使用,因為它在所有情況下的行為並不完全像傳統的 for 迴圈,而且還有需要考慮的潛在副作用。更多細節請參見 為什麼在陣列迭代中使用「for...in」是一個不好的主意?。

作為 for...in 的替代方法,現在也有 for...of。以下範例顯示了 for...of 迴圈與 for...in 迴圈之間的差異:

var myArray = [3, 5, 7];
myArray.foo = "hello";

for (var i in myArray) {
  console.log(i); // logs 0, 1, 2, "foo"
}

for (var i of myArray) {
  console.log(i); // logs 3, 5, 7
}

此外,你需要考慮到沒有任何版本的 Internet Explorer 支援 for...of(Edge 12+ 支援),以及 for...in 至少需要 Internet Explorer 10。


Array.prototype.forEach()

替代 for 迴圈的方法是 Array.prototype.forEach(),其語法如下:

myArray.forEach(function(value, key, myArray) {
    console.log(value);
});

Array.prototype.forEach() 受到所有現代瀏覽器的支持,以及 Internet Explorer 9 及更高版本。


圖書館

最後,許多實用程式庫也有自己的 foreach 變體。據我所知,三個最受歡迎的分別是這些:

jQuery.each(),在 jQuery 中:

$.each(myArray, function(key, value) {
    console.log(value);
});

_.each(),在 Underscore.js 中:

_.each(myArray, function(value, key, myArray) {
    console.log(value);
});

_.forEach(),在 Lodash 中:

_.forEach(myArray, function(value, key) {
    console.log(value);
});

答案#9

使用 while 迴圈...

var i = 0, item, items = ['one', 'two', 'three'];
while(item = items[i++]){
    console.log(item);
}

它會記錄:'one'、'two' 和 'three'

而對於反向順序,則是一個更高效的迴圈:

var items = ['one', 'two', 'three'], i = items.length;
while(i--){
    console.log(items[i]);
}

它會記錄:'三'、'二' 和 '一'

或者經典的 for 迴圈:

var items = ['one', 'two', 'three']
for(var i=0, l = items.length; i < l; i++){
    console.log(items[i]);
}

它記錄了:'一'、'二'、'三'

參考資料:Google Closure:如何不寫 JavaScript

答案#10

如果你想要一個簡潔的方式來寫一個快速迴圈,並且你可以反向迭代:

for (var i=myArray.length;i--;){
  var item=myArray[i];
}

這具有緩存長度的好處(類似於 for (var i=0, len=myArray.length; i<len; ++i),而不同於 for (var i=0; i<myArray.length; ++i)),同時需要輸入的字符更少。

甚至有時你應該反向迭代,例如當遍歷一個即時的 NodeList 並且你計劃在迭代過程中從 DOM 中移除項目時。

答案#11

在 JavaScript 中以 函數式編程方式遍歷陣列的一些使用案例:

1. 只需遍歷陣列

const myArray = [{x:100}, {x:200}, {x:300}];

myArray.forEach((element, index, array) => {
    console.log(element.x); // 100, 200, 300
    console.log(index); // 0, 1, 2
    console.log(array); // same myArray object 3 times
});

注意:從嚴格意義上說,Array.prototype.forEach() 並不是一種函數式的方法,因為它作為輸入參數的函數不應該返回值,因此不能被視為純函數。

2. 檢查陣列中的任何元素是否通過測試

const people = [
    {name: 'John', age: 23}, 
    {name: 'Andrew', age: 3}, 
    {name: 'Peter', age: 8}, 
    {name: 'Hanna', age: 14}, 
    {name: 'Adam', age: 37}];

const anyAdult = people.some(person => person.age >= 18);
console.log(anyAdult); // true

3. 轉換為新陣列

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => element.x);
console.log(newArray); // [100, 200, 300]

注意:map() 方法會建立一個新陣列,陣列中的元素是對呼叫陣列中的每個元素應用所提供函式的結果。

4. 總結某個特定屬性,並計算其平均值

const myArray = [{x:100}, {x:200}, {x:300}];

const sum = myArray.map(element => element.x).reduce((a, b) => a + b, 0);
console.log(sum); // 600 = 0 + 100 + 200 + 300

const average = sum / myArray.length;
console.log(average); // 200

5. 根據原始陣列創建一個新的陣列,但不修改原始陣列

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray= myArray.map(element => {
    return {
        ...element,
        x: element.x * 2
    };
});

console.log(myArray); // [100, 200, 300]
console.log(newArray); // [200, 400, 600]

6. 計算每個類別的數量

const people = [
    {name: 'John', group: 'A'}, 
    {name: 'Andrew', group: 'C'}, 
    {name: 'Peter', group: 'A'}, 
    {name: 'James', group: 'B'}, 
    {name: 'Hanna', group: 'A'}, 
    {name: 'Adam', group: 'B'}];

const groupInfo = people.reduce((groups, person) => {
    const {A = 0, B = 0, C = 0} = groups;
    if (person.group === 'A') {
        return {...groups, A: A + 1};
    } else if (person.group === 'B') {
        return {...groups, B: B + 1};
    } else {
        return {...groups, C: C + 1};
    }
}, {});

console.log(groupInfo); // {A: 3, C: 1, B: 2}

7. 根據特定條件取出陣列的子集

const myArray = [{x:100}, {x:200}, {x:300}];

const newArray = myArray.filter(element => element.x > 250);
console.log(newArray); // [{x:300}] 

注意:filter() 方法會建立一個新陣列,包含所有通過所提供函式測試的元素。

8. 排序陣列

const people = [
  { name: "John", age: 21 },
  { name: "Peter", age: 31 },
  { name: "Andrew", age: 29 },
  { name: "Thomas", age: 25 }
];

let sortByAge = people.sort(function (p1, p2) {
  return p1.age - p2.age;
});

console.log(sortByAge);

9. 在陣列中尋找一個元素

const people = [ {name: "john", age:23},
                {name: "john", age:43},
                {name: "jim", age:101},
                {name: "bob", age:67} ];

const john = people.find(person => person.name === 'john');
console.log(john);

Array.prototype.find() 方法返回陣列中第一個通過提供的測試函式的元素的值。

參考資料

  • Array.prototype.some()
  • Array.prototype.forEach()
  • Array.prototype.map()
  • Array.prototype.filter()
  • Array.prototype.sort()
  • 展開語法
  • Array.prototype.find()

答案#12

是的,你可以在 JavaScript 中使用迴圈做相同的事情,但不限於此。在 JavaScript 中,有很多方法可以對陣列進行迴圈處理。假設你有以下這個陣列,並且你想要對它進行迴圈處理:

var arr = [1, 2, 3, 4, 5];

這些是解決方案:

1) For 迴圈

在 JavaScript 中,for 迴圈是遍歷陣列的一種常見方式,但它不被認為是處理大型陣列的最快解決方案:

for (var i=0, l=arr.length; i<l; i++) {
  console.log(arr[i]);
}

2) while 迴圈

while 迴圈被認為是遍歷長陣列最快的方法,但它通常在 JavaScript 代碼中使用較少:

let i=0;

while (arr.length>i) {
    console.log(arr[i]);
    i++;
}

3) do while
do while 做的事情與 while 相同,但語法上有以下差異:

let i=0;
do {
  console.log(arr[i]);
  i++;
}
while (arr.length>i);

這些是進行 JavaScript 迴圈的主要方式,但還有一些其他方式可以做到。

我們也使用 for in 迴圈來遍歷 JavaScript 中的物件。

也看看 JavaScript 陣列上的 map()、filter()、reduce() 等函數。它們可能比使用 while 和 for 做事更快、更好。

如果你想更多地了解 JavaScript 中關於陣列的非同步函數,這是一篇不錯的文章。

函數式編程近來在開發界引起了相當大的關注。而且這是有充分理由的:函數式技術可以幫助你撰寫更具聲明性的程式碼,使其一目了然,更容易重構和測試。

函數式程式設計的基石之一是它對列表及列表操作的特殊使用。而這些東西正如其名稱所示:事物的陣列,以及你對它們所做的操作。但函數式思維對待它們的方式可能與你預期的略有不同。

本文將仔細探討我喜歡稱之為「三大」列表操作的內容:map、filter 和 reduce。理解這三個函數是能夠編寫乾淨函數式程式碼的重要步驟,並且為功能強大的函數式和反應式程式設計技術打開大門。

這也意味著你再也不需要寫 for 迴圈了。

閱讀更多>> 這裡:

答案#13

有一種方法可以做到這一點,在你的迴圈中幾乎沒有隱含的作用域,並且不需要額外的變數。

var i = 0,
     item;

// Note this is weak to sparse arrays or falsey values
for ( ; item = myStringArray[i++] ; ){
    item; // This is the string at the index.
}

或者如果你真的想取得 id 並且使用一個非常經典的 for 迴圈:

var i = 0,
    len = myStringArray.length; // Cache the length

for ( ; i < len ; i++ ){
    myStringArray[i]; // Don't use this if you plan on changing the length of the array
}

現代瀏覽器都支持在 Array 原型上的迭代器方法 forEach、map、reduce、filter 以及其他許多方法。

答案#14

在 JavaScript 中有多種方法來遍歷陣列。

一般循環:

var i;
for (i = 0; i < substr.length; ++i) {
    // Do something with `substr[i]`
}

ES5 的 forEach:

substr.forEach(function(item) {
    // Do something with `item`
});

jQuery.each:

jQuery.each(substr, function(index, item) {
    // Do something with `item` (or `this` is also `item` if you like)
});

查看這個以獲取詳細信息,或者你也可以查閱 MDN 了解在 JavaScript 中遍歷陣列,並且使用 jQuery 時查閱 jQuery 的 each 方法。

答案#15

陣列迴圈:

for(var i = 0; i < things.length; i++){
    var thing = things[i];
    console.log(thing);
}

物件迴圈:

for(var prop in obj){
    var propValue = obj[prop];
    console.log(propValue);
}

答案#16

我會強烈建議使用 Underscore.js 函式庫。它提供了各種函數,你可以用來遍歷陣列或集合。

例如:

_.each([1, 2, 3], function(num){ alert(num); });
=> alerts each number in turn...

答案#17

如果有人對陣列迭代可用的多種機制的性能方面感興趣,我已經準備了以下的 JSPerf 測試:

https://jsperf.com/fastest-array-iterator

結果:

傳統的 for() 迭代器,迄今為止是最快的方法,特別是當與 已緩存的陣列長度 一起使用時。

let arr = [1,2,3,4,5];

for(let i=0, size=arr.length; i<size; i++){
    // Do something
}

Array.prototype.forEach() 和 Array.prototype.map() 方法是最慢的近似值,可能是 函數調用開銷 的結果。

答案#18

如果你正在使用 jQuery 函式庫,請考慮使用 http://api.jquery.com/jQuery.each/

摘自文件說明:

jQuery.each( 集合, 回呼函數(indexInArray, valueOfElement) )

返回值: 物件

描述: 一個通用的迭代器函數,可用於無縫地遍歷對象和數組。具有長度屬性的數組和類數組對象(例如函數的 arguments 對象)會按數字索引從 0 遍歷到 length-1。其他對象則通過它們的命名屬性進行迭代。

$.each() 函數並不等同於 $(selector).each(),後者是專門用來迭代 jQuery 物件的。$.each() 函數可以用來迭代任何集合,無論它是映射(JavaScript 物件)還是陣列。在陣列的情況下,回呼函數每次會傳入一個陣列索引和對應的陣列值。(值也可以通過 this 關鍵字訪問,但即使它是簡單的字串或數字值,Javascript 也會將 this 的值包裝為 Object。)此方法會返回它的第一個參數,也就是被迭代的物件。

答案#19

我還沒看到這個變化,我個人最喜歡這個:

給定一個陣列:

var someArray = ["some", "example", "array"];

你可以遍歷它而不需要訪問長度屬性:

for (var i=0, item; item=someArray[i]; i++) {
  // item is "some", then "example", then "array"
  // i is the index of item in the array
  alert("someArray[" + i + "]: " + item);
}

請參見此 JsFiddle 演示: http://jsfiddle.net/prvzk/

這只適用於非稀疏的陣列。意思是陣列中的每個索引實際上都有一個值。不過,我發現在實際應用中,我幾乎很少在 JavaScript 中使用稀疏陣列……在這種情況下,使用物件作為映射/哈希表通常容易得多。如果你確實有一個稀疏陣列,並想要循環遍歷 0 到 length-1,你需要使用 for (var i=0; i<someArray.length; ++i) 的結構,但你仍然需要在循環內使用 if 來檢查當前索引的元素是否實際被定義。

另外,如 CMS 在下面的評論中所提到的,你只能在不包含任何假值的陣列上使用這個方法。範例中的字串陣列可以使用,但如果陣列中有空字串、或數字是 0 或 NaN 等,迴圈會過早中斷。實際上對我來說這幾乎不是問題,但這是一點需要注意的地方,因此在使用之前要考慮這個迴圈… 這可能會讓某些人不適合使用 :)

我喜歡這個迴圈的地方是:

  • 它很短,容易寫
  • 無需訪問(更不用說快取)length 屬性
  • 要訪問的項目會在迴圈主體中自動使用你選擇的名稱定義。
  • 非常自然地與 array.push 和 array.splice 結合,將陣列用作列表/堆疊

之所以這樣做有效,是因為陣列規範規定,當你從大於或等於陣列長度的索引讀取項目時,它會返回 undefined。當你向這樣的位置寫入時,它實際上會更新長度。

對我來說,這個結構最接近我喜愛的 Java 5 語法:

for (String item : someArray) {
}

... 另外還有了解循環中當前索引的好處

答案#20

陣列迭代有四種方式:

// 1: for

for (let i = 0; i < arr.length; ++i) {
  console.log(arr[i]);
}

// 2: forEach

arr.forEach((v, i) => console.log(v));

// 3: for in

for (let i in arr) {
  console.log(arr[i]);
}

// 4: for of

for (const v of arr) {
  console.log(v);
}

摘要:方案 1 和 3 創建額外變數,方案 2 創建額外函數上下文。最好的方法是第 4 種 - "for of"。

答案#21

密教的

let a= ["Hello", "World"];

while(a.length) { console.log( a.shift() ); }

性能測試

今天(2022-11-13)我在選定的解決方案上對 Chrome 107、Safari 15.2 和 Firefox 106 進行測試。

結論

  • 解法 C 和 D 在所有瀏覽器上對所有陣列都是快速或最快的。
  • 解法 A 和 B 在所有瀏覽器上對所有陣列都是最慢的

結果

詳細資訊

我進行三個測試:

  • 小型 - 適用於 2 元素陣列(像 OP 一樣) - 你可以在這裡運行它
  • 中等 - 適用於 10K 元素的陣列 - 你可以在這裡運行它
  • 大 - 適用於 100K 元素陣列 - 你可以在這裡運行它

以下程式碼片段展示了用於測試的程式碼。

function A(a) {
  let r=0;
  while(a.length) r+= a.shift().length;
  return r;
}

function B(a) {
  let r=0;
  for(i in a) r+= a[i].length;
  return r;
}

function C(a) {
  let r=0;
  for(x of a) r+= x.length;
  return r;
}

function D(a) {
  let r=0;
  for (i=0; i<a.length; ++i) r+= a[i].length;
  return r;

}

function E(a) {
  let r=0;
  a.forEach(x=> r+= x.length);
  return r;
}

let arr= ["Hello", "World!"];
[A,B,C,D,E].forEach(f => console.log(`${f.name}: ${f([...arr])}`))

以下是 Chrome 對中型陣列的範例結果:

答案#22

有一種方法可以只迭代自身的物件屬性,而不包括原型的屬性:

for (var i in array) if (array.hasOwnProperty(i)) {
    // Do something with array[i]
}

但它仍然會遍歷自定義的屬性。

在 JavaScript 中,任何自訂屬性都可以被指派給任何物件,包括陣列。

如果要遍歷稀疏陣列,應使用 for (var i=0; i < array.length; i++) if (i in array) 或在搭配 es5shim 時使用 array.forEach。

答案#23

最優雅且快速的方式

var arr = [1, 2, 3, 1023, 1024];
for (var value; value = arr.pop();) {
    value + 1
}

http://jsperf.com/native-loop-performance/8


已編輯(因為我錯了)


比較在遍歷一個包含100000個項目的數組時,每次對新值進行最小操作的方法。

  • http://jsben.ch/#/BQhED

準備:

<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script>
    Benchmark.prototype.setup = function() {
        // Fake function with minimal action on the value
        var tmp = 0;
        var process = function(value) {
            tmp = value; // Hold a reference to the variable (prevent engine optimisation?)
        };
        
        // Declare the test Array
        var arr = [];
        for (var i = 0; i < 100000; i++)
            arr[i] = i;
    };
</script>

測試:

<a href="http://jsperf.com/native-loop-performance/16" 
   title="http://jsperf.com/native-loop-performance/16"
><img src="http://i.imgur.com/YTrO68E.png" title="Hosted by imgur.com" /></a>

答案#24

在 JavaScript 中有幾種方法可以做到這一點。前兩個例子是 JavaScript 範例。第三個例子使用了一個 JavaScript 函式庫,也就是 jQuery,使用了 .each() 函數。

var myStringArray = ["hello", "World"];
for(var i in myStringArray) {
  alert(myStringArray[i]);
}

var myStringArray = ["hello", "World"];
for (var i=0; i < myStringArray.length; i++) {
  alert(myStringArray[i]);
}

var myStringArray = ["hello", "World"];
$.each(myStringArray, function(index, value){
  alert(value);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

答案#25

最佳化的方法是快取陣列的長度,並使用單一變數模式,用單一個 var 關鍵字初始化所有變數。

var i, max, myStringArray = ["Hello", "World"];
for (i = 0, max = myStringArray.length; i < max; i++) {
    alert(myStringArray[i]);

    // Do something
}

如果迭代的順序不重要,那麼你應該嘗試反向迴圈。它是最快的,因為它減少了條件測試的開銷,且遞減在一個語句中完成:

var i,myStringArray = ["item1","item2"];
for (i =  myStringArray.length; i--) {
    alert(myStringArray[i]);
}

或者更好且更乾淨的方法是使用 while 迴圈:

var myStringArray = ["item1","item2"],i = myStringArray.length;
while(i--) {
   // Do something with fruits[i]
}

答案#26

在 JavaScript 中,有很多方法可以迴圈陣列。

以下的代碼是熱門的

/** Declare inputs */
const items = ['Hello', 'World']

/** Solution 1. Simple for */
console.log('solution 1. simple for')

for (let i = 0; i < items.length; i++) {
  console.log(items[i])
}

console.log()
console.log()

/** Solution 2. Simple while */
console.log('solution 2. simple while')

let i = 0
while (i < items.length) {
  console.log(items[i++])
}

console.log()
console.log()

/** Solution 3. forEach*/
console.log('solution 3. forEach')

items.forEach(item => {
  console.log(item)
})

console.log()
console.log()

/** Solution 4. for-of*/
console.log('solution 4. for-of')

for (const item of items) {
  console.log(item)
}

console.log()
console.log()

答案#27

依我看,最好的方法是使用 Array.forEach 函數。如果你無法使用它,我會建議從 MDN 獲取 polyfill。為了使其可用,這肯定是在 JavaScript 中迭代數組最安全的方法。

Array.prototype.forEach()

所以正如其他人建議的,這幾乎總是你想要的:

var numbers = [1,11,22,33,44,55,66,77,88,99,111];
var sum = 0;
numbers.forEach(function(n){
  sum += n;
});

這確保了在處理陣列的範圍內,任何你需要的東西都保持在該範圍內,並且你只處理陣列的值,而不是物件屬性和其他成員,這正是 for .. in 所做的。

使用一般的 C 風格 for 迴圈在大多數情況下都適用。重要的是要記住,迴圈中的所有內容與程式的其他部分共享作用域,{ } 不會創建新的作用域。

因此:

var sum = 0;
var numbers = [1,11,22,33,44,55,66,77,88,99,111];

for(var i = 0; i<numbers.length; ++i){
  sum += numbers[i];
}

alert(i);

將輸出 "11" - 這可能是你想要的,也可能不是。

一個可用的 jsFiddle 範例: https://jsfiddle.net/workingClassHacker/pxpv2dh5/7/

答案#28

如果你想使用 jQuery,它的文件中有一個不錯的範例:

 $.each([ 52, 97 ], function( index, value ) {
      alert( index + ": " + value );
 });

答案#29

它並非百分之百相同,但相似:

   var myStringArray = ['Hello', 'World']; // The array uses [] not {}
    for (var i in myStringArray) {
        console.log(i + ' -> ' + myStringArray[i]); // i is the index/key, not the item
    }

答案#30

例如,我在 Firefox 控制台中使用了:

[].forEach.call(document.getElementsByTagName('pre'), function(e){ 
   console.log(e);
})

你可以使用 querySelectorAll 來獲得相同的結果

document.querySelectorAll('pre').forEach( (e) => { 
   console.log(e.textContent);
})
<pre>text 1</pre>
<pre>text 2</pre>
<pre>text 3</pre>