區別:1、forEach是一個迭代器,是負責遍歷(Array Set Map)可迭代物件的;而for是一種循環機制,只是能透過它遍歷出數組。 2.for循環中會用到一些中斷行為,對於優化數組遍歷查找是很好的,但由於forEach屬於迭代器,只能按序依次遍歷完成,所以不支援中斷行為。 3.forEach的循環起點只能為0,且不能進行人為幹預;而for循環不同,可以人為控制循環起點。
本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。
for迴圈和forEach本質差異
#for
迴圈是js提出時就有的迴圈方法。
forEach
是ES5提出的,掛載在可迭代物件原型上的方法,例如Array
Set
Map
。
forEach
是一個迭代器,負責遍歷可迭代物件。
那麼遍歷,迭代,可迭代物件分別是什麼呢。
遍歷:指的對資料結構的每一個成員進行有規律的且為一次存取的行為。
迭代:迭代是遞歸的特殊形式,是迭代器提供的一種方法,預設是依照一定順序逐一存取資料結構成員。迭代也是一種遍歷行為。
可迭代物件:ES6中引入了iterable
類型,Array
Set
Map
String
arguments
NodeList
都屬於iterable
,他們特質就是都擁有[Symbol.iterator]
方法,包含他的物件被認為是可迭代的iterable
。
forEach
其實是一個迭代器,他與for
迴圈本質上的差別是forEach
是負責遍歷(Array
Set
Map
)可迭代物件的,而for
迴圈是一種循環機制,只是能透過它遍歷出數組。
什麼是迭代器,當它被呼叫時就會產生一個迭代器物件(Iterator Object),它有一個.next()
方法,每次呼叫傳回一個物件{value:value,done:Boolean}
,value
傳回的是yield
後的回傳值,當yield
結束,done
變成true
,透過不斷呼叫並依序的迭代存取內部的值。
迭代器是一種特殊物件。 ES6規範中它的標誌是傳回物件的 next()
方法,迭代行為判斷在 done
之中。在不暴露內部表示的情況下,迭代器實現了遍歷。看程式碼
let arr = [1, 2, 3, 4] // 可迭代对象 let iterator = arr[Symbol.iterator]() // 调用 Symbol.iterator 后生成了迭代器对象 console.log(iterator.next()); // {value: 1, done: false} 访问迭代器对象的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}
我們看到了。只要是可迭代對象,呼叫內部的Symbol.iterator
都會提供一個迭代器,並根據迭代器返回的next
方法來存取內部,這也是for.. .of
的實作原理。
let arr = [1, 2, 3, 4] for (const item of arr) { console.log(item); // 1 2 3 4 }
把呼叫next
方法傳回物件的value
值並儲存在item
中,直到value
為undefined
跳出循環,所有可迭代物件可供for...of
消費。再來看看其他可迭代物件:
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}
所以可迭代物件中的Symbol.iterator
屬性被呼叫時都能產生迭代器,而forEach
也是生成一個迭代器,在內部的回呼函數中傳遞出每個元素的值
<span style="font-size: 20px;">#for</span>
##循環和<span style="font-size: 20px;"></span>#forEach
的語法差異
forEach
forEach
forEach
for
forEach 的參數我們真正了解
forEach
arr.forEach((self,index,arr) =>{},this)
self:
陣列目前遍歷的元素,預設從左往右依序取得陣列元素。index:
陣列目前元素的索引,第一個元素索引為0,依序類別推。arr:
目前遍歷的陣列。this:
回呼函數中this指向。let arr = [1, 2, 3, 4]; arr.forEach(function (self, index, arr) { console.log(`当前元素为${self}索引为${index},属于数组${arr}`); }, person)
我們可以利用
arr 實作陣列去重:###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
对函数进行中断或跳出循环的操作,我们在 for
循环中会用到一些中断行为,对于优化数组遍历查找是很好的,但由于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; //报错 }; }); arr.forEach((self,index) => { console.log(self); if (self === 2) { continue; //报错 }; });
如果我一定要在 forEach
中跳出循环呢?其实是有办法的,借助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
并不会报错,但是不会生效
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
中我们无法控制 index
的值,它只会无脑的自增直至大于数组的 length
跳出循环。所以也无法删除自身进行index
重置,先看一个简单例子:
let arr = [1,2,3,4] arr.forEach((item, index) => { console.log(item); // 1 2 3 4 index++; });
index
不会随着函数体内部对它的增减而发生变化。在实际开发中,遍历数组同时删除某项的操作十分常见,在使用forEach
删除时要注意。
for
循环可以控制循环起点如上文提到的 forEach
的循环起点只能为0不能进行人为干预,而for
循环不同:
let arr = [1, 2, 3, 4], i = 1, length = arr.length; for (; i < length; i++) { console.log(arr[i]) // 2 3 4 };
那之前的数组遍历并删除滋生的操作就可以写成
let arr = [1, 2, 1], i = 0, length = arr.length; for (; i < length; i++) { // 删除数组中所有的1 if (arr[i] === 1) { arr.splice(i, 1); //重置i,否则i会跳一位 i--; }; }; console.log(arr); // [2] //等价于 var arr1 = arr.filter(index => index !== 1); console.log(arr1) // [2]
<span style="font-size: 18px;">for</span>
循环和<span style="font-size: 18px;">forEach</span>
的性能区别
在性能对比方面我们加入一个 map
迭代器,它与 filter
一样都是生成新数组。
对比 for
forEach
map
的性能在浏览器环境中都是什么样的:
性能比较:for > forEach > map 在chrome 62 和 Node.js v9.1.0环境下:for
循环比 forEach
快1倍,forEach
比 map
快20%左右。
原因分析for
:for循环没有额外的函数调用栈和上下文,所以它的实现最为简单。
forEach
:对于forEach来说,它的函数签名中包含了参数和上下文,所以性能会低于 for
循环。
map
:map
最慢的原因是因为 map
会返回一个新的数组,数组的创建和赋值会导致分配内存空间,因此会带来较大的性能开销。
如果将map
嵌套在一个循环中,便会带来更多不必要的内存消耗。
当大家使用迭代器遍历一个数组时,如果不需要返回一个新数组却使用 map
是违背设计初衷的。
【相关推荐:javascript视频教程、编程视频】
以上是es6中for和foreach的差別是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!