詳細談談JavaScript中循環之間的差異

前言

在 JavaScript 中使用循環時,需要正確定義兩個關鍵內容:可枚舉屬性(enumerable properties)和可迭代對象(iterable objects)。

可枚舉的屬性

可枚舉對象的一個定義特征是,當我們通過賦值運算符將屬性賦值給對象時,我們將內部可枚舉標志(enumerable)設置為 true。這是默認值。

但是,我們可以通過將其設置為 false 來更改此行為。

經驗法則是,可枚舉屬性總是出現在 for…in 循環中。

讓我們看看這一點:

const users = {}
users.languages = 'JavaScript'
​
Object.getOwnPropertyDescriptor(users, 'languages')
// output -> { value: 'JavaScript', writable: true, enumerable: true, configurable: true }
​
// 在循環中對我們使用的屬性進行更多的控制
Object.defineProperty(users, 'role', { value: 'Admin', writable: true, enumerable: false })
​
for (const item in users) {
  console.log(item) // languages
}

可以看到,我們為 users 變量添加瞭一個 languages 屬性,使用 Object.getOwnPropertyDescriptor 方法輸出 languages 屬性描述符的 enumerable 屬性為 true。

使用 Object.defineProperty 為添加 role 屬性,並將 enumerable 設置為 false,在 for…in 循環中並沒有輸出 role 屬性。即 for…in 循環中的屬性為可枚舉屬性。

可迭代對象

如果一個對象定義瞭它的迭代行為,那麼它是可迭代的。在本例中,將在 for…of 構造中循環的值將定義其迭代行為。可迭代的內置類型包括 Array、String、Set 和 Map 對象不可迭代,因為它沒有指定 @iterator 方法。

基本上,在 JavaScript 中,所有可迭代對象都是可枚舉對象,但並非所有可枚舉對象都是可迭代對象。

這裡有一種概念化的方法:for…in 查找數據中的對象,而 for…of 查找重復序列。

讓我們看看這一切在與 Array 數據類型一起使用時的效果:

const languages = ['JavaScript', 'Python', 'Go']
​
// 與 for...in 循環一起使用
for (const language in languages) {
  console.log(language)
}
// output
// 0
// 1
// 2
​
// 與 for...of 循環一起使用
for (const language of languages) {
  console.log(author)
}
// output -> JavaScript Python Go

在使用這種構造時,需要牢記的一點是,如果調用瞭 typeof,並且輸出為 object,那麼您可以使用 for…in 循環。

讓我們看看這個對 languages 變量的操作:

typeof languages // "object" -> 因此我們可以在中使用 for

乍一看,這可能令人驚訝,但需要註意的是,數組是一種特殊類型的對象,以索引為鍵。知道 for…in 將在構造中查找對象可以極大地幫助我們。當 for…in 循環找到一個對象時,它將在每個鍵上循環。

我們可以將 for ..in 循環在 languages 數組上的方式可視化如下:

const languages = {
  0: 'JavaScript',
  1: 'Python',
  2: 'Go'
}

註意:如果它可以被追蹤到一個對象(或者從對象原型鏈繼承它),for…in 將以沒有特定順序遍歷鍵。

同時,如果它實現瞭一個迭代器 for.. of 構造,它將在每次迭代中循環遍歷該值。

在 forEach 與 map 方法

雖然 forEach 和 map 方法可以用來實現相同的目標,但它們的行為和性能特性有所不同。

在基本級別,當函數被調用時,它們都會接收一個回調作為參數。

考慮以下片段:

const scoresEach = [2, 4 ,8, 16, 32]
const scoresMap = [2, 4 ,8, 16, 32]
const square = (num) => num * num

讓我們詳細介紹一下它們在操作上的一些差異。

forEach 返回 undefined,而 map 返回一個新的 array:

let newScores = []
const resultWithEach = scoresEach.forEach(score => {
  const newScore = square(score)
  newScores.push(newScore)
})
​
const resultWithMap = scoresMap.map(square)
​
console.log(resultWithEach) // undefined
console.log(resultWithMap) // [4, 16, 64, 256, 1024]

Map 是一個純函數,同時 forEach 執行一些突變:

console.log(newScores) // [4, 16, 64, 256, 1024]

在我看來,map 支持函數式編程范式。我們不必總是執行突變來獲得期望的結果,不像 forEach,我們必須突變 newScores 變量。在每次運行時,當提供相同的輸入時,map 函數將產生相同的結果。同時,forEach 對應項將從上一個突變的先前值中提取。

鏈式調用

使用 map 可以進行鏈式調用,因為返回的結果是一個數組。因此,可以對結果立即調用任何其他數組方法。換句話說,我們可以調用 filter、reduce、some 等方法。這在 forEach 中是不可能的,因為返回的值為 undefined 的。

性能

map 方法的性能往往優於 forEach 方法。

檢查使用 map 和 forEach 實現的等效代碼塊的性能。平均而言,您將看到 map 函數的執行速度至少快 50%。

結論

在上面討論的所有循環結構中,給我們最多控制的是 for..of 循環。我們可以將其與關鍵字 return、continue 和 break 一起使用。這意味著我們可以指定對數組中的每個元素要發生什麼,以及是否要提前離開或跳過。

到此這篇關於JavaScript中循環之間差異的文章就介紹到這瞭,更多相關JavaScript循環差異內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: