本篇文章透過範例程式碼分析的方式為大家講解了JavaScript數組操作困難點以及需要注意的地方,一起學習參考下吧。
以下內容是學習JavaScript陣列的時候總結的經驗以及需要注意的要點。
不要用 for_in 遍歷陣列
這是 JavaScript 初學者常見的誤解。 for_in 用於遍歷物件中包含原型鏈上的所有可枚舉的(enumerable)的 key,本來不是為遍歷陣列而存在。
使用 for_in 遍歷陣列有三點問題:
1、遍歷順序不固定
JavaScript 引擎不保證物件的遍歷順序。當把數組當作普通物件遍歷時同樣不保證遍歷出的索引順序。
2、會遍歷出物件原型鏈上的值。
如果你改變了陣列的原型物件(例如 polyfill)而沒有將其設為 enumerable: false
,for_in 會把這些東西遍歷出來。
3、運作效率低。
儘管理論上 JavaScript 使用物件的形式儲存數組,JavaScript 引擎還是會對數組這個非常常用的內建物件特別優化。 https://jsperf.com/for-in-vs-...
可以看到使用for_in 遍歷數組要比使用下標遍歷數組慢50 倍以上
PS:你可能是想找for_of
不要用JSON.parse(JSON.stringify()) 深拷貝數組
有人使用JSON 中深拷貝物件或陣列。這雖然在多數情況是個簡單方便的手段,但也可能引發未知bug,因為:會使某些特定值轉換為null
NaN, undefined, Infinity 對於JSON 中不支援的這些值,會在序列化JSON 時被轉換為null,反序列化回來後自然也就是null
會丟失值為undefined 的鍵值對
JSON 序列化時會忽略值為undefined 的key,反序列化回來後自然也就丟失了
會將Date 物件轉換為字串
JSON 不支援物件類型,對於JS 中Date 物件的處理方式為轉換為ISO8601 格式的字串。然而反序列化並不會把時間格式的字串轉換為 Date 物件
運行效率低。
作為原生函數,JSON.stringify
和 JSON.parse
自身操作 JSON 字串的速度是很快的。然而為了深拷貝數組把物件序列化成 JSON 再反序列化回來完全沒有必要。
我花了一些時間寫了一個簡單的深拷貝數組或物件的函數,測試發現運行速度差不多是使用JSON 中轉的6 倍左右,順便還支援了TypedArray、RegExp 的物件的複製
https://jsperf.com/deep-clone...
#不要用arr.find 取代arr.some
Array.prototype.find
是ES2015 新增的陣列查找函數,與Array.prototype.some
有相似之處,但不能取代後者。
Array.prototype.find
傳回第一個符合條件的值,直接拿這個值做if
判斷是否存在,如果這個符合條件的值剛好是0 怎麼辦?
arr.find
是找到數組中的值後對其進一步處理,一般用於物件數組的情況;arr.some
是檢查存在性;兩者不可混用。
不要用arr.map 取代arr.forEach
也是一個JavaScript 初學者常犯的錯誤,他們往往並沒有分辨Array.prototype.map
和Array.prototype.forEach
的實際意義。
map
中文叫做 映射
,它透過將某個序列依序執行某個函數匯出另一個新的序列。這個函數通常是不含副作用的,更不會修改原始的陣列(所謂純函數)。
forEach
就沒有那麼多說法,它就是簡單的把數組中所有項目都用某個函數處理一遍。由於 forEach
沒有傳回值(傳回 undefined),所以它的回呼函數通常是包含副作用的,否則這個 forEach
寫了毫無意義。
確實 map
比 forEach
更強大,但是 map
會建立一個新的數組,佔用記憶體。如果你不用map
的回傳值,那你就該使用forEach
補充:心得補充
ES6 以前,遍歷數組主要是兩種方法:手寫迴圈用下標迭代,使用Array.prototype.forEach
。前者萬能,效率最高,可就是寫起來比較繁瑣——它不能直接取得到數組中的值。
筆者個人是喜歡後者的:可以直接取得到迭代的下標和值,而且函數式風格(注意FP 注重的是不可變資料結構,forEach 天生為副作用存在,所以只有FP 的形而沒有神)寫起來爽快無比。但是!不知各位同學注意過沒有:forEach 一旦開始就停不下來了。 。 。
forEach 接受一個回呼函數,你可以提前 return
,相當於手寫迴圈中的 continue
。但是你不能 break
——因為回呼函數中沒有循環讓你去 break
:
[1, 2, 3, 4, 5].forEach(x => { console.log(x); if (x === 3) { break; // SyntaxError: Illegal break statement } });
解決方案還是有的。其他函數式程式語言例如 scala
就遇到了類似問題,它提供了一個函數
break,作用是拋出一個例外。
我們可以仿照這樣的做法,來實作arr.forEach
的break
:
try { [1, 2, 3, 4, 5].forEach(x => { console.log(x); if (x === 3) { throw 'break'; } }); } catch (e) { if (e !== 'break') throw e; // 不要勿吞异常。。。 }
還有其他方法,例如用Array.prototype.some
取代Array.prototype.forEach
。
考慮Array.prototype.some 的特性,當some
找到一個符合條件的值(回呼函數傳回true
)時會立即終止循環,利用這樣的特性可以模擬break
:
[1, 2, 3, 4, 5].some(x => { console.log(x); if (x === 3) { return true; // break } // return undefined; 相当于 false });
some
的回傳值被忽略掉了,它已經脫離了判斷數組中是否有元素符合給出的條件這一原始的含義。
在 ES6 前,筆者主要使用該法(其實因為 Babel 代碼膨脹的緣故,現在也偶爾使用),ES6 不一樣了,我們有了 for...of。 for...of
是真正的循環,可以break
:
for (const x of [1, 2, 3, 4, 5]) { console.log(x); if (x === 3) { break; } }
但是有個問題,for...of
似乎拿不到循環的下標。其實JavaScript 語言制定者想到了這個問題,可以如下解決:
for (const [index, value] of [1, 2, 3, 4, 5].entries()) { console.log(`arr[${index}] = ${value}`); }
Array.prototype.entries
for...of
和forEach
的效能測試:https://jsperf.com/array-fore... Chrome 中for...of
要快一點哦
以上是有關JavaScript數組操作困難(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!