首頁 >web前端 >js教程 >JavaScript 中數組操作注意點基礎

JavaScript 中數組操作注意點基礎

小云云
小云云原創
2017-12-18 16:10:001367瀏覽

本文主要跟大家分享JavaScript 中陣列操作注意點基礎,不要用 for_in 遍歷陣列這是 JavaScript 初學者常見的誤區。 for_in 用於遍歷物件中包含原型鏈上的所有可枚舉的(enumerable)的 key,本來不是為遍歷陣列而存在。

使用for_in 遍歷陣列有三點問題:

  1. 遍歷順序不固定

#JavaScript 引擎不保證物件的遍歷順序。當把數組當作普通物件遍歷時同樣不保證遍歷出的索引順序。

  1. 會遍歷出物件原型鏈上的值。

如果你改變了陣列的原型物件(例如polyfill)而沒有將其設為<span style="font-size: 14px;">enumerable: false</span>,for_in 會把這些東西遍歷出來。

  1. 運作效率低。

儘管理論上 JavaScript 使用物件的形式儲存數組,JavaScript 引擎還是會對數組這個非常常用的內建物件特別優化。 https://jsperf.com/for-in-vs-...  
可以看到使用for_in 遍歷數組要比使用下標遍歷數組慢50 倍以上

PS:你可能是想找for_of

不要用JSON.parse(JSON.stringify()) 深拷貝數組

有人使用JSON中深拷貝物件或陣列。這雖然在多數情況是個簡單方便的手段,但也可能引發未知bug,因為:

  1. 會使某些特定值轉換為<span style="font-size: 14px;">#null</span>

NaN, undefined, Infinity 對於JSON 中不支援的這些值,會在序列化JSON 時轉換為null,反序列化回來後自然也就是null

  1. 會遺失值為undefined 的鍵值對

JSON 序列化時會忽略值為undefined 的key,反序列化回來後自然也就遺失了

  1. 會將Date 對象轉換為字串

JSON 不支援物件類型,對於JS 中Date 物件的處理方式為轉換為ISO8601 格式的字串。然而反序列化並不會把時間格式的字串轉換為 Date 物件

  1. #運行效率低。

作為原生函數,<span style="font-size: 14px;">#JSON.stringify</span> <span style="font-size: 14px;">JSON.parse</span> 自身操作JSON 字串的速度是很快的。然而為了深拷貝數組把物件序列化成 JSON 再反序列化回來完全沒有必要。

我花了一些時間寫了一個簡單的深拷貝數組或物件的函數,測試發現運行速度差不多是使用JSON 中轉的6 倍左右,順便還支援了TypedArray、 RegExp 的物件的複製

https://jsperf.com/deep-clone...

不要用arr.find 取代arr .some

<span style="font-size: 14px;">Array.prototype.find</span> 是ES2015 中新增的陣列查找函數,與<span style="font-size: 14px;"> #Array.prototype.some</span> 有相似之處,但不能取代後者。

<span style="font-size: 14px;">Array.prototype.find</span># 傳回第一個符合條件的值,直接拿這個值做<span style="font-size: 14px;">if</span> 判斷是否存在,如果這個符合條件的值剛好是0 怎麼辦?

<span style="font-size: 14px;">arr.find</span> 是在找到數組中的值後對其進一步處理,一般用於物件數組的情況; <span style="font-size: 14px;">arr.some</span> 是檢查存在性;兩者不可混用。

不要用arr.map 取代arr.forEach

也是一個JavaScript 初學者常犯的錯誤,他們往往沒有分辨<span style="font-size: 14px;">Array.prototype.map</span><span style="font-size: 14px;">Array.prototype.forEach</span> 的實際意義。

<span style="font-size: 14px;">map</span> 中文叫做<span style="font-size: 14px;">映射</span>,它透過將某個序列依序執行某個函數匯出另一個新的序列。這個函數通常是不含副作用的,更不會修改原始的陣列(所謂純函數)。

<span style="font-size: 14px;">forEach</span> 就沒有那麼多說法,它就是簡單的把陣列中所有項目都用某個函數處理一遍。由於<span style="font-size: 14px;">forEach</span> 沒有回傳值(回傳undefined),所以它的回呼函數通常是包含副作用的,否則這個<span style="font-size: 14px;"># forEach</span> 寫了毫無意義。

確實<span style="font-size: 14px;">map</span># 比<span style="font-size: 14px;">forEach</span> 更強大,但是<span style="font-size: 14px;">map</span> 會建立一個新的數組,佔用記憶體。如果你不用<span style="font-size: 14px;">map</span> 的回傳值,那你就該使用<span style="font-size: 14px;">forEach</span>

補:forEach 與break

ES6 以前,遍歷陣列主要就是兩種方法:手寫迴圈用下標迭代,使用<span style="font-size: 14px;">Array.prototype.forEach</span>。前者萬能,效率最高,可就是寫起來比較繁瑣——它不能直接取得到數組中的值。

筆者個人是喜歡後者的:可以直接取得到迭代的下標和值,而且函數式風格(注意FP 注重的是不可變資料結構,forEach 天生為副作用存在,所以只有FP 的形而沒有神)寫起來爽快無比。但是!不知各位同學注意過沒有:forEach 一旦開始就停不下來了。 。 。

forEach 接受一個回呼函數,你可以提前<span style="font-size: 14px;">#return</span>,相當於手寫迴圈中的 <span style="font-size: 14px;">continue</span>。但是你不能<span style="font-size: 14px;">break</span>——因為回呼函數中沒有循環讓你去<span style="font-size: 14px;">break</span>

<span style="font-size: 14px;">[1, 2, 3, 4, 5].forEach(x => {<br>  console.log(x);<br>  if (x === 3) {<br>    break;  // SyntaxError: Illegal break statement<br>  }<br>});<br></span>

解決方案還是有的。其他函數式程式語言例如<span style="font-size: 14px;">scala</span> 就遇到了類似問題,它提供了一個函數
#break,作用是拋出一個異常。

JavaScript 中數組操作注意點基礎

我們可以仿照這樣的做法,來實作<span style="font-size: 14px;">arr.forEach</span><span style="font-size: 14px;">break</span>

<span style="font-size: 14px;">try {<br>  [1, 2, 3, 4, 5].forEach(x => {<br>    console.log(x);<br>    if (x === 3) {<br>      throw 'break';<br>    }<br>  });<br>} catch (e) {<br>  if (e !== 'break') throw e; // 不要勿吞异常。。。<br>}<br></span>

噁心的一B對不對。還有其他方法,例如用<span style="font-size: 14px;">Array.prototype.some</span># 取代<span style="font-size: 14px;">Array.prototype.forEach</span> #。

考慮Array.prototype.some 的特性,當<span style="font-size: 14px;"></span># 找到一個符合條件的值(回呼函數返回<span style="font-size: 14px;">true</span>)時會立即終止循環,利用這樣的特性可以模擬<span style="font-size: 14px;">break</span>

#
<span style="font-size: 14px;">[1, 2, 3, 4, 5].some(x => {<br>  console.log(x);<br>  if (x === 3) {<br>    return true; // break<br>  }<br>  // return undefined; 相当于 false<br>});<br></span>

<span style="font-size: 14px;">some</span> 的返回值被忽略掉了,它已经脱离了判断数组中是否有元素符合给出的条件这一原始的含义。

在 ES6 前,笔者主要使用该法(其实因为 Babel 代码膨胀的缘故,现在也偶尔使用),ES6 不一样了,我们有了 for...of。<span style="font-size: 14px;">for...of</span> 是真正的循环,可以 <span style="font-size: 14px;">break</span>

<span style="font-size: 14px;">for (const x of [1, 2, 3, 4, 5]) {<br>  console.log(x);<br>  if (x === 3) {<br>    break;<br>  }<br>}<br></span>

但是有个问题,<span style="font-size: 14px;">for...of</span> 似乎拿不到循环的下标。其实 JavaScript 语言制定者想到了这个问题,可以如下解决:

<span style="font-size: 14px;">for (const [index, value] of [1, 2, 3, 4, 5].entries()) {<br>  console.log(`arr[${index}] = ${value}`);<br>}<br></span>

Array.prototype.entries

<span style="font-size: 14px;">for...of</span><span style="font-size: 14px;">forEach</span> 的性能测试:https://jsperf.com/array-fore... Chrome 中 <span style="font-size: 14px;">for...of</span> 要快一些哦

以上是JavaScript 中數組操作注意點基礎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn