成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試官:有了 for 循環 為什么還要 forEach ?

開發 前端
js中那么多循環,for for...in for...of forEach,有些循環感覺上是大同小異今天我們討論下for循環和forEach的差異。

[[441197]]

 js中那么多循環,for for...in for...of forEach,有些循環感覺上是大同小異今天我們討論下for循環和forEach的差異。我們從幾個維度展開討論:

  •  for循環和forEach的本質區別。
  •  for循環和forEach的語法區別。
  •  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 循環是一種循環機制,只是能通過它遍歷出數組。

再來聊聊究竟什么是迭代器,還記得之前提到的 Generator 生成器,當它被調用時就會生成一個迭代器對象(Iterator Object),它有一個 .next()方法,每次調用返回一個對象{value:value,done:Boolean},value返回的是 yield 后的返回值,當 yield 結束,done 變為 true,通過不斷調用并依次的迭代訪問內部的值。

迭代器是一種特殊對象。ES6規范中它的標志是返回對象的 next() 方法,迭代行為判斷在 done 之中。在不暴露內部表示的情況下,迭代器實現了遍歷。看代碼 

  1. let arr = [1, 2, 3, 4]  // 可迭代對象  
  2. let iterator = arr[Symbol.iterator]()  // 調用 Symbol.iterator 后生成了迭代器對象  
  3. console.log(iterator.next()); // {value: 1, done: false}  訪問迭代器對象的next方法  
  4. console.log(iterator.next()); // {value: 2, done: false}  
  5. console.log(iterator.next()); // {value: 3, done: false}  
  6. console.log(iterator.next()); // {value: 4, done: false}  
  7. console.log(iterator.next()); // {value: undefined, done: true} 

我們看到了。只要是可迭代對象,調用內部的 Symbol.iterator 都會提供一個迭代器,并根據迭代器返回的next 方法來訪問內部,這也是 for...of 的實現原理。 

  1. let arr = [1, 2, 3, 4]  
  2. for (const item of arr) {  
  3.     console.log(item); // 1 2 3 4  
  4.  

把調用 next 方法返回對象的 value 值并保存在 item 中,直到 value 為 undefined 跳出循環,所有可迭代對象可供for...of消費。再來看看其他可迭代對象: 

  1. function num(params) {  
  2.     console.log(arguments); // Arguments(6) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]  
  3.     let iterator = arguments[Symbol.iterator]()  
  4.     console.log(iterator.next()); // {value: 1, done: false}  
  5.     console.log(iterator.next()); // {value: 2, done: false}  
  6.     console.log(iterator.next()); // {value: 3, done: false}  
  7.     console.log(iterator.next()); // {value: 4, done: false}  
  8.     console.log(iterator.next()); // {value: undefined, done: true}  
  9.  
  10. num(1, 2, 3, 4)  
  11. let set = new Set('1234')  
  12. set.forEach(item => {  
  13.     console.log(item); // 1 2 3 4  
  14. })  
  15. let iterator = set[Symbol.iterator]()  
  16. console.log(iterator.next()); // {value: 1, done: false}  
  17. console.log(iterator.next()); // {value: 2, done: false}  
  18. console.log(iterator.next()); // {value: 3, done: false} 
  19. console.log(iterator.next()); // {value: 4, done: false}  
  20. console.log(iterator.next()); // {value: undefined, done: true} 

所以我們也能很直觀的看到可迭代對象中的 Symbol.iterator 屬性被調用時都能生成迭代器,而 forEach 也是生成一個迭代器,在內部的回調函數中傳遞出每個元素的值。

(感興趣的同學可以搜下 forEach 源碼, Array Set Map 實例上都掛載著 forEach ,但網上的答案大多數是通過 length 判斷長度, 利用for循環機制實現的。但在 Set Map 上使用會報錯,所以我認為是調用的迭代器,不斷調用 next,傳參到回調函數。由于網上沒查到答案也不妄下斷言了,有答案的人可以評論區留言)

for循環和forEach的語法區別

了解了本質區別,在應用過程中,他們到底有什么語法區別呢?

  1. forEach 的參數。
  2.  forEach 的中斷。
  3.  forEach 刪除自身元素,index不可被重置。
  4.  for 循環可以控制循環起點。

forEach 的參數

我們真正了解 forEach 的完整傳參內容嗎?它大概是這樣: 

  1. arr.forEach((self,index,arr) =>{},this) 

self: 數組當前遍歷的元素,默認從左往右依次獲取數組元素。

index: 數組當前元素的索引,第一個元素索引為0,依次類推。

arr: 當前遍歷的數組。

this: 回調函數中this指向。 

  1. let arr = [1, 2, 3, 4];  
  2. let person = {  
  3.     name: '技術直男星辰'  
  4. };  
  5. arr.forEach(function (self, index, arr) {  
  6.     console.log(`當前元素為${self}索引為${index},屬于數組${arr}`);  
  7.     console.log(this.name+='真帥');  
  8. }, person) 

我們可以利用 arr 實現數組去重: 

  1. let arr1 = [1, 2, 1, 3, 1];  
  2. let arr2 = [];  
  3. arr1.forEach(function (self, index, arr) {  
  4.     arr.indexOf(self) === index ? arr2.push(self) : null;  
  5. });  
  6. console.log(arr2);   // [1,2,3] 

forEach 的中斷

在js中有break return continue 對函數進行中斷或跳出循環的操作,我們在 for循環中會用到一些中斷行為,對于優化數組遍歷查找是很好的,但由于forEach屬于迭代器,只能按序依次遍歷完成,所以不支持上述的中斷行為。 

  1. let arr = [1, 2, 3, 4],  
  2.     i = 0 
  3.     length = arr.length;  
  4. for (; i < length; i++) {  
  5.     console.log(arr[i]); //1,2  
  6.     if (arr[i] === 2) {  
  7.         break;  
  8.     };  
  9. };  
  10. arr.forEach((self,index) => {  
  11.     console.log(self);  
  12.     if (self === 2) {  
  13.         break; //報錯  
  14.     };  
  15. });  
  16. arr.forEach((self,index) => {  
  17.     console.log(self);  
  18.     if (self === 2) {  
  19.         continue; //報錯  
  20.     };  
  21. }); 

如果我一定要在 forEach 中跳出循環呢?其實是有辦法的,借助try/catch: 

  1. try {  
  2.     var arr = [1, 2, 3, 4];  
  3.     arr.forEach(function (item, index) {  
  4.         //跳出條件  
  5.         if (item === 3) {  
  6.             throw new Error("LoopTerminates");  
  7.         }  
  8.         //do something  
  9.         console.log(item);  
  10.     });  
  11. } catch (e) {  
  12.     if (e.message !== "LoopTerminates") throw e;  
  13. }; 

若遇到 return 并不會報錯,但是不會生效 

  1. let arr = [1, 2, 3, 4];  
  2. function find(array, num) {     
  3.     array.forEach((self, index) => {     
  4.          if (self === num) {    
  5.              return index;      
  6.          };    
  7.      });  
  8. }; 
  9.  let index = find(arr, 2);// undefined 

forEach 刪除自身元素,index不可被重置

在 forEach 中我們無法控制 index 的值,它只會無腦的自增直至大于數組的 length 跳出循環。所以也無法刪除自身進行index重置,先看一個簡單例子: 

  1. let arr = [1,2,3,4]  
  2. arr.forEach((item, index) => {    
  3.     console.log(item); // 1 2 3 4   
  4.     index++;  
  5. }); 

index不會隨著函數體內部對它的增減而發生變化。在實際開發中,遍歷數組同時刪除某項的操作十分常見,在使用forEach刪除時要注意。

for 循環可以控制循環起點

如上文提到的 forEach 的循環起點只能為0不能進行人為干預,而for循環不同: 

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

那之前的數組遍歷并刪除滋生的操作就可以寫成 

  1. let arr = [1, 2, 1],  
  2.     i = 0 
  3.     length = arr.length;  
  4. for (; i < length; i++) {  
  5.     // 刪除數組中所有的1  
  6.     if (arr[i] === 1) {  
  7.         arr.splice(i, 1);  
  8.         //重置i,否則i會跳一位  
  9.         i--;  
  10.     };  
  11. };  
  12. console.log(arr); // [2]  
  13. //等價于  
  14. var arrarr1 = arr.filter(index => index !== 1);  
  15. console.log(arr1) // [2] 

for循環和forEach的性能區別

在性能對比方面我們加入一個 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 是違背設計初衷的。在我前端合作開發時見過很多人只是為了遍歷數組而用 map 的: 

  1. let data = [];  
  2. let data2 = [1,2,3];  
  3. data2.map(item=>data.push(item)); 

寫在最后:這是面試遇到的一個問題,當時只知道語法區別。并沒有從可迭代對象,迭代器,生成器和性能方面,多角度進一步區分兩者的異同,也希望能把一個簡單的問題從多角度展開細講,讓大家正在搞懂搞透徹。   

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2022-06-07 08:39:35

RPCHTTP

2022-07-06 13:48:24

RedisSentinel機制

2019-08-05 14:23:43

DockerKubernetes容器

2024-07-11 10:41:07

HTTPSHTTP文本傳輸協議

2023-08-11 17:13:39

JavaScrip

2020-11-25 09:36:17

HTTPRPC遠程

2023-12-06 09:10:28

JWT微服務

2020-10-24 15:50:54

Java值傳遞代碼

2021-02-19 10:02:57

HTTPSJava安全

2023-01-12 09:01:01

MongoDBMySQL

2021-01-21 07:53:29

面試官Promis打印e

2023-12-11 12:03:14

Python工具元組

2023-09-04 08:28:34

JavaScripforEach 循環

2025-04-01 00:00:00

項目CRUD單例模式

2022-09-13 08:44:02

IP網絡MAC地址

2024-04-16 08:26:18

IP地址MAC地址

2023-12-20 14:35:37

Java虛擬線程

2023-02-17 08:10:24

2021-07-06 07:27:45

React元素屬性

2022-12-27 08:39:54

MySQL主鍵索引
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产日韩欧美激情 | 国产精品视频观看 | 一级一级一级毛片 | 蜜桃在线一区二区三区 | 欧美成年人 | 91精品久久久久久久久 | a级大片免费观看 | 日韩欧美中文字幕在线视频 | 一区二区三区精品 | 久久av一区二区三区 | 亚洲高清视频一区二区 | 永久看片| 亚洲精品日韩在线 | 看毛片的网站 | 无码国模国产在线观看 | 中文字幕av网址 | 精品一级| 欧美成人不卡 | 久久久69| 超级黄色一级片 | 91色视频在线 | 国产精品无码久久久久 | 中文字幕精品一区二区三区精品 | 天天操网| 本道综合精品 | 日本国产一区二区 | 尤物在线精品视频 | 一区二区精品视频 | 国产一区视频在线 | 亚洲一区二区国产 | 很黄很污的网站 | 中文字幕一区二区三区在线乱码 | 成年免费大片黄在线观看岛国 | 久久久久亚洲 | 国产中文视频 | 国产精品久久久久久婷婷天堂 | 91欧美| 超碰av在线 | 久在线视频 | 欧美一级在线 | 国产乱码精品1区2区3区 |