來源:juejin.im/post/5ea63f3ef265da47b177b4b6
幾種遍歷方法中for執行最快,它沒有任何額外的函式呼叫堆疊和背景關系,但在實際開發中我們要結合語意話、可讀性和程式性能,去選擇究竟使用哪種方案,下面來看for , foreach , map , for...in , for...of五種方法現場battle,
for
我是最早出現的一方遍歷陳述句,在座的各位需稱我一聲爺爺,我能滿足開發人員的絕大多數的需求,
// 遍歷陣列
let arr = [1,2,3];
for(let i = 0;i < arr.length;i++){
console.log(i) // 索引,陣列下標
console.log(arr[i]) // 陣列下標所對應的元素
}
// 遍歷物件
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i = 0, keys=Object.keys(profile); i < keys.length;i++){
console.log(keys[i]) // 物件的鍵值
console.log(profile[keys[i]]) // 物件的鍵對應的值
}
// 遍歷字串
let str = "abcdef";
for(let i = 0;i < str.length ;i++){
console.log(i) // 索引 字串的下標
console.log(str[i]) // 字串下標所對應的元素
}
// 遍歷DOM 節點
let articleParagraphs = document.querySelectorAll('.article > p');
for(let i = 0;i<articleParagraphs.length;i++){
articleParagraphs[i].classList.add("paragraph");
// 給class名為“article”節點下的 p 標簽添加一個名為“paragraph” class屬性,
}
forEach
我是ES5版本發布的,按升序為陣列中含有效值的每一項執行一次 callback 函式,那些已洗掉或者未初始化的項將被跳過(例如在稀疏陣列上),我是 for 回圈的加強版,
// 遍歷陣列
let arr = [1,2,3];
arr.forEach(i => console.log(i))
// logs 1
// logs 2
// logs 3
// 直接輸出了陣列的元素
//遍歷物件
let profile = {name:"April",nickname:"二十七刻",country:"China"};
let keys = Object.keys(profile);
keys.forEach(i => {
console.log(i) // 物件的鍵值
console.log(profile[i]) // 物件的鍵對應的值
})
map
我也是ES5版本發布的,我可以創建一個新陣列,新陣列的結果是原陣列中的每個元素都呼叫一次提供的函式后的回傳值,
let arr = [1,2,3,4,5];
let res = arr.map(i => i * i);
console.log(res) // logs [1, 4, 9, 16, 25]
for...in列舉
我是ES5版本發布的,以任意順序遍歷一個物件的除Symbol以外的可列舉屬性,
// 遍歷物件
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i in profile){
let item = profile[i];
console.log(item) // 物件的鍵值
console.log(i) // 物件的鍵對應的值
// 遍歷陣列
let arr = ['a','b','c'];
for(let i in arr){
let item = arr[i];
console.log(item) // 陣列下標所對應的元素
console.log(i) // 索引,陣列下標
// 遍歷字串
let str = "abcd"
for(let i in str){
let item = str[i];
console.log(item) // 字串下標所對應的元素
console.log(i) // 索引 字串的下標
}
for...of迭代
我是ES6版本發布的,在可迭代物件(包括 Array,Map,Set,String,TypedArray,arguments 物件等等)上創建一個迭代回圈,呼叫自定義迭代鉤子,并為每個不同屬性的值執行陳述句,
// 迭代陣列陣列
let arr = ['a','b','c'];
for(let item of arr){
console.log(item)
}
// logs 'a'
// logs 'b'
// logs 'c'
// 迭代字串
let str = "abc";
for (let value of str) {
console.log(value);
}
// logs 'a'
// logs 'b'
// logs 'c'
// 迭代map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]
for (let entry of iterable) {
console.log(entry);
}
// logs ["a", 1]
// logs ["b", 2]
// logs ["c", 3]
// 迭代map獲取鍵值
for (let [key, value] of iterable) {
console.log(key)
console.log(value);
}
// 迭代set
let iterable = new Set([1, 1, 2, 2, 3, 3,4]);
for (let value of iterable) {
console.log(value);
}
// logs 1
// logs 2
// logs 3
// logs 4
// 迭代 DOM 節點
let articleParagraphs = document.querySelectorAll('.article > p');
for (let paragraph of articleParagraphs) {
paragraph.classList.add("paragraph");
// 給class名為“article”節點下的 p 標簽添加一個名為“paragraph” class屬性,
}
// 迭代arguments類陣列物件
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, 2, 3);
// logs:
// 1
// 2
// 3
// 迭代型別陣列
let typeArr = new Uint8Array([0x00, 0xff]);
for (let value of typeArr) {
console.log(value);
}
// logs:
// 0
// 255
經過第一輪的自我介紹和技能展示后,我們了解到:
for陳述句是最原始的回圈陳述句,定義一個變數i(數字型別,表示陣列的下標),按照一定的條件,對i進行回圈累加,條件通常為回圈物件的長度,當超過長度就停止回圈,因為物件無法判斷長度,所以搭配Object.keys()使用,forEachES5 提出,自稱是for陳述句的加強版,可以發現它比for陳述句在寫法上簡單了很多,但是本質上也是陣列的回圈,forEach每個陣列元素執行一次 callback 函式,也就是呼叫它的陣列,因此,不會改變原陣列,回傳值是undefine,mapES5 提出,給原陣列中的每個元素都按順序呼叫一次 callback 函式,生成一個新陣列,不修改呼叫它的原陣列本身,回傳值是新的陣列,for...inES5 提出,遍歷物件上的可列舉屬性,包括原型物件上的屬性,且按任意順序進行遍歷,也就是順序不固定,遍歷陣列時把陣列的下標當作鍵值,此時的i是個字串型的,它是為遍歷物件屬性而構建的,不建議與陣列一起使用,for...ofES6 提出,只遍歷可迭代物件的資料,
能力甄別
作為一個程式員,僅僅認識他們是遠遠不夠的,在實際開發中鑒別他們各自的優缺點,因地制宜的使用他們,揚長避短,從而提高程式的整體性能才是能力之所在,
關于跳出回圈體
在回圈中滿足一定條件就跳出回圈體,或者跳過不符合條件的資料繼續回圈其它資料,是經常會遇到的需求,常用的陳述句是break 與 continue,
簡單的說一下二者的區別,就當復習好了,
break陳述句是跳出當前回圈,并執行當前回圈之后的陳述句;continue陳述句是終止當前回圈,并繼續執行下一次回圈;
注意:forEach 與map 是不支持跳出回圈體的,其它三種方法均支持,
原理 :查看forEach實作原理,就會理解這個問題,
Array.prototype.forEach(callbackfn [,thisArg]{
}
傳入的function是這里的回呼函式,在回呼函式里面使用break肯定是非法的,因為break只能用于跳出回圈,回呼函式不是回圈體,
在回呼函式中使用return,只是將結果回傳到上級函式,也就是這個for回圈中,并沒有結束for回圈,所以return也是無效的,
map() 同理,
map()鏈式呼叫
map() 方法是可以鏈式呼叫的,這意味著它可以方便的結合其它方法一起使用,例如:reduce(), sort(), filter() 等,但是其它方法并不能做到這一點,forEach()的回傳值是undefined,所以無法鏈式呼叫,
// 將元素乘以本身,再進行求和,
let arr = [1, 2, 3, 4, 5];
let res1 = arr.map(item => item * item).reduce((total, value) => total + value);
console.log(res1) // logs 55 undefined"
for...in會遍歷出原型物件上的屬性
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
console.log(i);
}
// logs
// 0
// 1
// 2
// foo
// arrCustom
// objCustom
然而在實際的開發中,我們并不需要原型物件上的屬性,這種情況下我們可以使用hasOwnProperty() 方法,它會回傳一個布林值,指示物件自身屬性中是否具有指定的屬性(也就是,是否有指定的鍵),如下:
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i);
}
}
// logs
// 0
// 1
// 2
// foo
// 可見陣列本身的屬性還是無法擺脫,此時建議使用 forEach
對于純物件的遍歷,選擇for..in列舉更方便;對于陣列遍歷,如果不需要知道索引for..of迭代更合適,因為還可以中斷;如果需要知道索引,則forEach()更合適;對于其他字串,類陣列,型別陣列的迭代,for..of更占上風更勝一籌,但是注意低版本瀏覽器的是配性,
性能
有興趣的讀者可以找一組資料自行測驗,文章就直接給出結果了,并做相應的解釋,
for > for-of > forEach > map > for-in
for回圈當然是最簡單的,因為它沒有任何額外的函式呼叫堆疊和背景關系;for...of只要具有Iterator介面的資料結構,都可以使用它迭代成員,它直接讀取的是鍵值,forEach,因為它其實比我們想象得要復雜一些,它實際上是array.forEach(function(currentValue, index, arr), thisValue)它不是普通的 for 回圈的語法糖,還有諸多引數和背景關系需要在執行的時候考慮進來,這里可能拖慢性能;map()最慢,因為它的回傳值是一個等長的全新的陣列,陣列創建和賦值產生的性能開銷很大,for...in需要窮舉物件的所有屬性,包括自定義的添加的屬性也能遍歷到,且for...in的key是String型別,有轉換程序,開銷比較大,
總結
在實際開發中我們要結合語意話、可讀性和程式性能,去選擇究竟使用哪種方案,
如果你需要將陣列按照某種規則映射為另一個陣列,就應該用 map,
如果你需要進行簡單的遍歷,用 forEach 或者 for of,
如果你需要對迭代器進行遍歷,用 for of,
如果你需要過濾出符合條件的項,用 filterr,
如果你需要先按照規則映射為新陣列,再根據條件過濾,那就用一個 map 加一個 filter,
總之,因地制宜,因時而變,千萬不要因為過分追求性能,而忽略了語意和可讀性,在您的統治之下,他們5個只能是各自發揮長處,誰都別想稱霸,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.別在再滿屏的 if/ else 了,試試策略模式,真香!!
3.臥槽!Java 中的 xx ≠ null 是什么新語法?
4.Spring Boot 2.5 重磅發布,黑暗模式太炸了!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/300899.html
標籤:其他
上一篇:centos6安裝python3
