首先須要知道對於數組和可迭代對象的遍歷方法,咱們須要從不一樣的維度進行對比,方法的功能性,方法的應用場景,方法的兼容性,方法的效率,方法的返回值以及是否改變原始數組。深層次的咱們能夠思考如何實現這些方法,而且考慮到低版本瀏覽器的兼容性。若是要分group的話,能夠這麼分:forEach()與map()
,every()與some()
,filter()與find()findIndex()
,keys()、values()與entries()
,reduce()與reduceRight()
。固然對於的常規的遍歷方法,好比for
,while
與do-while
,$.each
,$(selector).each
這裏就不贅述了,原生的方法固然是主角,但也要看應用場景,項目中使用新增的遍歷方法確實能提升代碼效率,jquery的方法固然也有借鑑的地方,可是在如今的前端項目中用到的較少。 javascript
指定數組的每一項元素都執行了一次傳入的函數,返回值爲undefined,thisArg可選,用來當作fn函數內的this對象;forEach不能在低版本IE(6~8)中使用,兼容寫法請參考 Polyfill。得益於鴨式辨型,雖然forEach不能直接遍歷對象,但它能夠經過call方式遍歷類數組對象前端
var o = {0:1, 1:3, 2:5, length:3};
Array.prototype.forEach.call(o,function(value, index, obj){
console.log(value,index,obj);
obj[index] = value * value;
},o);
// 1 0 Object {0: 1, 1: 3, 2: 5, length: 3}
// 3 1 Object {0: 1, 1: 3, 2: 5, length: 3}
// 5 2 Object {0: 1, 1: 9, 2: 5, length: 3}
console.log(o); // Object {0: 1, 1: 9, 2: 25, length: 3}
複製代碼
能夠說這麼多遍歷函數中,forEach()要注意的地方最多,有如下要注意的:java
this都是指向調用方法的數組;jquery
var array = [1, 3, 5];
var obj = {name:'cc'};
var sReturn = array.forEach(function(value, index, array){
array[index] = value * value;
console.log(this.name); // cc被打印了三次
},obj);
console.log(array); // [1, 9, 25], 可見原數組改變了
console.log(sReturn); // undefined, 可見返回值爲undefined
複製代碼
沒有返回值,它老是返回undefined值,即便你return了一個值git
var solt = arr1.forEach((v,i,t) => {
console.log(v)
})
console.log(solt) // undefined
複製代碼
不能 中途退出循環,不能用break,會報錯的,只能用return退出本次回調,進行下一次回調github
var arr1 = [1, 2, 3, 4, 5]
// 使用break會報錯
arr1.forEach((v,i,arr) => {
console.log(v)
if(v === 3) {
break
}
})
//SyntaxError: Illegal break statement
// return false 也無效
arr1.forEach((v,i,arr) => {
console.log(v)
if(v === 3) {
return false
console.log('-----')//不會執行
}
})
/* 1 2 3 4 5 */
複製代碼
使用箭頭函數,thisArg
參數會被忽略數組
var arr1 = [1, 2, 3]
var arr2 = [7, 8, 9]
arr1.forEach((v, i, arr) => {
console.log(this)
})
// window
// window
// window
arr1.forEach((v, i, arr) => {
console.log(this)
}, arr2)
// window
// window
// window
複製代碼
map() 方法遍歷數組,使用傳入函數處理每一個元素,並返回函數的返回值組成的新數組。其在低版本IE(6~8)的兼容寫法請參考 Polyfill。瀏覽器
every() 方法使用傳入的函數測試全部元素,只要其中有一個函數返回值爲 false,那麼該方法的結果爲 false;若是所有返回 true,那麼該方法的結果才爲 true;every 同樣不能在低版本IE(6~8)中使用,兼容寫法請參考 Polyfill。bash
如下是鴨式辨型的寫法:函數
var o = {0:10, 1:8, 2:25, length:3};
var bool = Array.prototype.every.call(o,function(value, index, obj){
return value >= 8;
},o);
console.log(bool); // true
複製代碼
some()方法 測試數組元素時,只要有一個函數返回值爲 true,則該方法返回 true,若所有返回 false,則該方法返回 false;some 的鴨式辨型寫法能夠參照every,一樣它也不能在低版本IE(6~8)中使用,兼容寫法請參考 Polyfill。
filter() 方法使用傳入的函數測試全部元素,並返回全部經過測試的元素組成的新數組。它就比如一個過濾器,篩掉不符合條件的元素。filter同樣支持鴨式辨型,具體請參考every方法鴨式辨型寫法。其在低版本IE(6~8)的兼容寫法請參考 Polyfill。
var array = [18, 9, 10, 35, 80];
var array2 = array.filter(function(value, index, array){
return value > 20;
});
console.log(array2); // [35, 80]
複製代碼
通常會使用for-in
來遍歷對象的屬性的,不過屬性須要 enumerable,才能被讀取到. for-in
循環只遍歷可枚舉屬性。通常經常使用來遍歷對象,包括非整數類型的名稱和繼承的那些原型鏈上面的屬性也能被遍歷。像 Array和 Object使用內置構造函數所建立的對象都會繼承自Object.prototype和String.prototype的不可枚舉屬性就不能遍歷了
var obj = {
name: 'test',
color: 'red',
day: 'sunday',
number: 5
}
for (var key in obj) {
console.log(obj[key])
}
複製代碼
for-of
語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上建立一個迭代循環,調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句。只要是一個iterable的對象,就能夠經過for-of
來迭代.
var arr = [{name:'bb'},5,'test']
for (item of arr) {
console.log(item)
}
複製代碼
find() 方法基於ECMAScript 2015(ES6)規範,返回數組中第一個知足條件的元素(若是有的話), 若是沒有,則返回undefined。
findIndex() 方法也基於ECMAScript 2015(ES6)規範,它返回數組中第一個知足條件的元素的索引(若是有的話)不然返回-1。
var array = [1, 3, 5, 7, 8, 9, 10];
function f(value, index, array){
return value%2==0; // 返回偶數
}
function f2(value, index, array){
return value > 20; // 返回大於20的數
}
console.log(array.find(f)); // 8
console.log(array.find(f2)); // undefined
console.log(array.findIndex(f)); // 4
console.log(array.findIndex(f2)); // -1
複製代碼
判斷一個數組是否包含一個指定的值,若是包含則返回 true,不然返回false。
var array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
var pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false
複製代碼
reduce() 方法接收一個方法做爲累加器,數組中的每一個值(從左至右) 開始合併,最終爲一個值;initialValue 指定第一次調用 fn 的第一個參數。;當 fn 第一次執行時:
var array = [1, 2, 3, 4];
var s = array.reduce(function(previousValue, value, index, array){
return previousValue * value;
},1);
console.log(s); // 24
// ES6寫法更加簡潔
array.reduce((p, v) => p * v); // 24
複製代碼
reduceRight() 方法接收一個方法做爲累加器,數組中的每一個值(從右至左)開始合併,最終爲一個值。除了與reduce執行方向相反外,其餘徹底與其一致.
entries() 方法基於ECMAScript 2015(ES6)規範,返回一個數組迭代器對象,該對象包含數組中每一個索引的鍵值對。
var array = ["a", "b", "c"];
var iterator = array.entries();
console.log(iterator.next().value); // [0, "a"]
console.log(iterator.next().value); // [1, "b"]
console.log(iterator.next().value); // [2, "c"]
console.log(iterator.next().value); // undefined, 迭代器處於數組末尾時, 再迭代就會返回undefined
複製代碼
entries 也受益於鴨式辨型,以下:
var o = {0:"a", 1:"b", 2:"c", length:3};
var iterator = Array.prototype.entries.call(o);
console.log(iterator.next().value); // [0, "a"]
console.log(iterator.next().value); // [1, "b"]
console.log(iterator.next().value); // [2, "c"]
複製代碼
keys() 方法基於ECMAScript 2015(ES6)規範,返回一個數組索引的迭代器。(瀏覽器實際實現可能會有調整)
var array = ["abc", "xyz"];
var iterator = array.keys();
console.log(iterator.next()); // Object {value: 0, done: false}
console.log(iterator.next()); // Object {value: 1, done: false}
console.log(iterator.next()); // Object {value: undefined, done: false}
複製代碼
要注意的是索引迭代器會包含那些沒有對應元素的索引
var array = ["abc", , "xyz"];
var sparseKeys = Object.keys(array);
var denseKeys = [...array.keys()];
console.log(sparseKeys); // ["0", "2"]
console.log(denseKeys); // [0, 1, 2]
複製代碼
values() 方法基於ECMAScript 2015(ES6)規範,返回一個數組迭代器對象,該對象包含數組中每一個索引的值。其用法基本與上述 entries 方法一致。兼容性方面,目前沒有瀏覽器實現了該方法
var array = ["abc", "xyz"];
var iterator = array.values();
console.log(iterator.next().value);//abc
console.log(iterator.next().value);//xyz
複製代碼
該方法基於ECMAScript 2015(ES6)規範,同 values 方法功能相同。
var array = ["abc", "xyz"];
var iterator = array[Symbol.iterator]();
console.log(iterator.next().value); // abc
console.log(iterator.next().value); // xyz
複製代碼
數組遍歷的方法中
返回值有的是數組,有的是一個值,有的是布爾值,有的是一個數組迭代器對象;
every,some是用來判斷的,返回一個布爾值;filter,find,findIndex都是用來篩選的,而且返回知足條件的元素或元素組成的數組,不一樣的是,filter返回全部知足條件的數組成的數組,find與findIndex返回第一個知足條件的元素或元素的索引;
Array.prototype的全部方法均具備鴨式辨型這種神奇的特性,它們不只能夠用來處理數組對象,還能夠處理類數組對象;
ES6中的新增方法應用對象更普遍,能夠用於全部的具備Iterator接口的可迭代對象,好比數組,類數組對象,Map和Set結構;
對於各類遍歷方法的效率問題,這篇文章(詳解JS遍歷)對JS中遍歷方法進行了測試比較,得出的結論就是JavaScript原生的方法的效率高於各類封裝的方法;
forEach()與map()的不一樣點:
map()
建立了新數組,不改變原數組;forEach()
能夠改變原數組。
遇到空缺的時候map()
雖然會跳過,但保留空缺;forEach()
遍歷時跳過空缺,不保留空缺。
map()
按照原始數組元素順序依次處理元素;forEach()
遍歷數組的每一個元素,將元素傳給回調函數。
用forEach()
爲數組中的每一個元素添加屬性
var arr = [
{name : 'kiwi', age : 12},
{name : 'sasa', age : 22},
{name : 'alice', age : 32},
{name : 'joe', age : 42}
]
arr.forEach(function(ele, index){
if(index > 2){
ele.sex = 'boy';
}else{
ele.sex = 'girl';
}
return arr1
})
console.log(arr)//元素組發生改變
//[{name: "kiwi", age: 12, sex: "girl"},{name: "sasa", age: 22, sex: "girl"},{name: "alice", age: 32, sex: "gi
複製代碼
** 遇到空缺比較**
['a', , 'b'].forEach(function(ele,index){
console.log(ele + '. ' + index);
})
//0.a
//2.b
['a', , 'b'].map(function(ele,index){
console.log(ele + '. ' + index);
})
//['0.a', , '2.b']
複製代碼
for-of與for-in的不一樣點
for...of
循環不會循環對象的key,只會循環出數組的value,所以for...of
不能循環遍歷普通對象,對普通對象的屬性遍歷推薦使用for...in
。若是實在想用for...of
來遍歷普通對象的屬性的話,能夠經過和Object.keys()
搭配使用,先獲取對象的全部key的數組而後遍歷:var student={
name:'wujunchuan',
age:22,
locate:{
country:'china',
city:'xiamen',
school:'XMUT'
}
}
for(var key of Object.keys(student)){
//使用Object.keys()方法獲取對象key的數組
console.log(key+": "+student[key]);
}
/* //若是是下面這樣,會報錯提示 「TypeError: student is not iterable」 for(var key of student){ //使用Object.keys()方法獲取對象key的數組 console.log(key+": "+student[key]); } */
複製代碼
for...in
循環出的是key,for...of
循環出的是value;推薦在循環對象屬性的時候,使用for...in
,在遍歷數組的時候的時候使用for...of
。注意,for...of
是ES6新引入的特性。修復了ES5引入的for...in
的不足參考: