區(qū)別:1、forEach是一個(gè)迭代器,是負(fù)責(zé)遍歷(Array Set Map)可迭代對(duì)象的;而for是一種循環(huán)機(jī)制,只是能通過(guò)它遍歷出數(shù)組。2、for循環(huán)中會(huì)用到一些中斷行為,對(duì)于優(yōu)化數(shù)組遍歷查找是很好的,但由于forEach屬于迭代器,只能按序依次遍歷完成,所以不支持中斷行為。3、forEach的循環(huán)起點(diǎn)只能為0,且不能進(jìn)行人為干預(yù);而for循環(huán)不同,可以人為控制循環(huán)起點(diǎn)。
前端(vue)入門到精通課程:進(jìn)入學(xué)習(xí)
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點(diǎn)擊使用
本教程操作環(huán)境:windows7系統(tǒng)、ECMAScript 6版、Dell G3電腦。
for循環(huán)和forEach本質(zhì)區(qū)別
for
循環(huán)是js提出時(shí)就有的循環(huán)方法。
forEach
是ES5提出的,掛載在可迭代對(duì)象原型上的方法,例如Array
Set
Map
。
forEach
是一個(gè)迭代器,負(fù)責(zé)遍歷可迭代對(duì)象。
那么遍歷,迭代,可迭代對(duì)象分別是什么呢。
-
遍歷:指的對(duì)數(shù)據(jù)結(jié)構(gòu)的每一個(gè)成員進(jìn)行有規(guī)律的且為一次訪問(wèn)的行為。
-
迭代:迭代是遞歸的一種特殊形式,是迭代器提供的一種方法,默認(rèn)情況下是按照一定順序逐個(gè)訪問(wèn)數(shù)據(jù)結(jié)構(gòu)成員。迭代也是一種遍歷行為。
-
可迭代對(duì)象:ES6中引入了
iterable
類型,Array
Set
Map
String
arguments
NodeList
都屬于iterable
,他們特點(diǎn)就是都擁有[Symbol.iterator]
方法,包含他的對(duì)象被認(rèn)為是可迭代的iterable
。
forEach
其實(shí)是一個(gè)迭代器,他與 for
循環(huán)本質(zhì)上的區(qū)別是 forEach
是負(fù)責(zé)遍歷(Array
Set
Map
)可迭代對(duì)象的,而 for
循環(huán)是一種循環(huán)機(jī)制,只是能通過(guò)它遍歷出數(shù)組。
什么是迭代器,當(dāng)它被調(diào)用時(shí)就會(huì)生成一個(gè)迭代器對(duì)象(Iterator Object),它有一個(gè) .next()
方法,每次調(diào)用返回一個(gè)對(duì)象{value:value,done:Boolean}
,value
返回的是 yield
后的返回值,當(dāng) yield
結(jié)束,done
變?yōu)?true
,通過(guò)不斷調(diào)用并依次的迭代訪問(wèn)內(nèi)部的值。
迭代器是一種特殊對(duì)象。ES6規(guī)范中它的標(biāo)志是返回對(duì)象的 next()
方法,迭代行為判斷在 done
之中。在不暴露內(nèi)部表示的情況下,迭代器實(shí)現(xiàn)了遍歷。看代碼
let arr = [1, 2, 3, 4] // 可迭代對(duì)象 let iterator = arr[Symbol.iterator]() // 調(diào)用 Symbol.iterator 后生成了迭代器對(duì)象 console.log(iterator.next()); // {value: 1, done: false} 訪問(wèn)迭代器對(duì)象的next方法 console.log(iterator.next()); // {value: 2, done: false} console.log(iterator.next()); // {value: 3, done: false} console.log(iterator.next()); // {value: 4, done: false} console.log(iterator.next()); // {value: undefined, done: true}
我們看到了。只要是可迭代對(duì)象,調(diào)用內(nèi)部的 Symbol.iterator
都會(huì)提供一個(gè)迭代器,并根據(jù)迭代器返回的next
方法來(lái)訪問(wèn)內(nèi)部,這也是 for...of
的實(shí)現(xiàn)原理。
let arr = [1, 2, 3, 4] for (const item of arr) { console.log(item); // 1 2 3 4 }
把調(diào)用 next
方法返回對(duì)象的 value
值并保存在 item
中,直到 value
為 undefined
跳出循環(huán),所有可迭代對(duì)象可供for...of
消費(fèi)。再來(lái)看看其他可迭代對(duì)象:
function num(params) { console.log(arguments); // Arguments(6) [1, 2, 3, 4, callee: ?, Symbol(Symbol.iterator): ?] let iterator = arguments[Symbol.iterator]() console.log(iterator.next()); // {value: 1, done: false} console.log(iterator.next()); // {value: 2, done: false} console.log(iterator.next()); // {value: 3, done: false} console.log(iterator.next()); // {value: 4, done: false} console.log(iterator.next()); // {value: undefined, done: true} } num(1, 2, 3, 4) let set = new Set('1234') set.forEach(item => { console.log(item); // 1 2 3 4 }) let iterator = set[Symbol.iterator]() console.log(iterator.next()); // {value: 1, done: false} console.log(iterator.next()); // {value: 2, done: false} console.log(iterator.next()); // {value: 3, done: false} console.log(iterator.next()); // {value: 4, done: false} console.log(iterator.next()); // {value: undefined, done: true}
所以可迭代對(duì)象中的 Symbol.iterator
屬性被調(diào)用時(shí)都能生成迭代器,而 forEach
也是生成一個(gè)迭代器,在內(nèi)部的回調(diào)函數(shù)中傳遞出每個(gè)元素的值
for
循環(huán)和forEach
的語(yǔ)法區(qū)別
了解了本質(zhì)區(qū)別,在應(yīng)用過(guò)程中,他們到底有什么語(yǔ)法區(qū)別呢?
-
forEach
的參數(shù)。 -
forEach
的中斷。 -
forEach
刪除自身元素,index不可被重置。 -
for
循環(huán)可以控制循環(huán)起點(diǎn)。
forEach
的參數(shù)
我們真正了解 forEach
的完整傳參內(nèi)容嗎?它大概是這樣:
arr.forEach((self,index,arr) =>{},this)
-
self: 數(shù)組當(dāng)前遍歷的元素,默認(rèn)從左往右依次獲取數(shù)組元素。
-
index: 數(shù)組當(dāng)前元素的索引,第一個(gè)元素索引為0,依次類推。
-
arr: 當(dāng)前遍歷的數(shù)組。
-
this: 回調(diào)函數(shù)中this指向。
let arr = [1, 2, 3, 4]; arr.forEach(function (self, index, arr) { console.log(`當(dāng)前元素為${self}索引為${index},屬于數(shù)組${arr}`); }, person)
我們可以利用 arr
實(shí)現(xiàn)數(shù)組去重:
let arr1 = [1, 2, 1, 3, 1]; let arr2 = []; arr1.forEach(function (self, index, arr) { arr.indexOf(self) === index ? arr2.push(self) : null; }); console.log(arr2); // [1,2,3]
forEach
的中斷
在js中有break
return
continue
對(duì)函數(shù)進(jìn)行中斷或跳出循環(huán)的操作,我們?cè)?for
循環(huán)中會(huì)用到一些中斷行為,對(duì)于優(yōu)化數(shù)組遍歷查找是很好的,但由于forEach
屬于迭代器,只能按序依次遍歷完成,所以不支持上述的中斷行為。
let arr = [1, 2, 3, 4], i = 0, length = arr.length; for (; i < length; i++) { console.log(arr[i]); //1,2 if (arr[i] === 2) { break; }; }; arr.forEach((self,index) => { console.log(self); if (self === 2) { break; //報(bào)錯(cuò) }; }); arr.forEach((self,index) => { console.log(self); if (self === 2) { continue; //報(bào)錯(cuò) }; });
如果我一定要在 forEach
中跳出循環(huán)呢?其實(shí)是有辦法的,借助try/catch
:
try { var arr = [1, 2, 3, 4]; arr.forEach(function (item, index) { //跳出條件 if (item === 3) { throw new Error("LoopTerminates"); } //do something console.log(item); }); } catch (e) { if (e.message !== "LoopTerminates") throw e; };
若遇到 return
并不會(huì)報(bào)錯(cuò),但是不會(huì)生效
let arr = [1, 2, 3, 4]; function find(array, num) { array.forEach((self, index) => { if (self === num) { return index; }; }); }; let index = find(arr, 2);// undefined
forEach
刪除自身元素,index不可被重置
在 forEach
中我們無(wú)法控制 index
的值,它只會(huì)無(wú)腦的自增直至大于數(shù)組的 length
跳出循環(huán)。所以也無(wú)法刪除自身進(jìn)行index
重置,先看一個(gè)簡(jiǎn)單例子:
let arr = [1,2,3,4] arr.forEach((item, index) => { console.log(item); // 1 2 3 4 index++; });
index
不會(huì)隨著函數(shù)體內(nèi)部對(duì)它的增減而發(fā)生變化。在實(shí)際開(kāi)發(fā)中,遍歷數(shù)組同時(shí)刪除某項(xiàng)的操作十分常見(jiàn),在使用forEach
刪除時(shí)要注意。
for
循環(huán)可以控制循環(huán)起點(diǎn)
如上文提到的 forEach
的循環(huán)起點(diǎn)只能為0不能進(jìn)行人為干預(yù),而for
循環(huán)不同:
let arr = [1, 2, 3, 4], i = 1, length = arr.length; for (; i < length; i++) { console.log(arr[i]) // 2 3 4 };
那之前的數(shù)組遍歷并刪除滋生的操作就可以寫成
let arr = [1, 2, 1], i = 0, length = arr.length; for (; i < length; i++) { // 刪除數(shù)組中所有的1 if (arr[i] === 1) { arr.splice(i, 1); //重置i,否則i會(huì)跳一位 i--; }; }; console.log(arr); // [2] //等價(jià)于 var arr1 = arr.filter(index => index !== 1); console.log(arr1) // [2]
for
循環(huán)和forEach
的性能區(qū)別
在性能對(duì)比方面我們加入一個(gè) map
迭代器,它與 filter
一樣都是生成新數(shù)組。
對(duì)比 for
forEach
map
的性能在瀏覽器環(huán)境中都是什么樣的:
性能比較:for > forEach > map 在chrome 62 和 Node.js v9.1.0環(huán)境下:for
循環(huán)比 forEach
快1倍,forEach
比 map
快20%左右。
原因分析for
:for循環(huán)沒(méi)有額外的函數(shù)調(diào)用棧和上下文,所以它的實(shí)現(xiàn)最為簡(jiǎn)單。
forEach
:對(duì)于forEach來(lái)說(shuō),它的函數(shù)簽名中包含了參數(shù)和上下文,所以性能會(huì)低于 for
循環(huán)。
map
:map
最慢的原因是因?yàn)?map
會(huì)返回一個(gè)新的數(shù)組,數(shù)組的創(chuàng)建和賦值會(huì)導(dǎo)致分配內(nèi)存空間,因此會(huì)帶來(lái)較大的性能開(kāi)銷。
如果將map
嵌套在一個(gè)循環(huán)中,便會(huì)帶來(lái)