寫在最前:js中包含的迭代器(遍歷器)比較多,博主認為整理在一塊一起學習,進行對比,有助于記憶,便整理出這么一篇長文,文章有點長,強烈建議收藏,反復查閱!
目錄
1.定義
2.js中內置的迭代器(陳述句篇)
Iterator
for...in
for...of
for await ... of
3.js中內置的迭代器(函式篇)
forEach()
map()
every()
some()
find()
findIndex()
filter()
reduce()
reduceRight()
1.定義
迭代器模式是指提供一種方法順序訪問一個聚合物件中的各個元素,而又不需要暴露該物件的內部表示,
迭代器模式可以把迭代的程序從業務邏輯中分離出來,在使用迭代器模式之后,即使不關心物件的內部構造,也可以按順序訪問其中的每個元素,
JavaScript中內置的有關迭代器的使用非常多,接下來我們來看看都有哪些?
2.js中內置的迭代器(陳述句篇)
以下為迭代器陳述句篇,其中包括Iterator、for...in、for...of、for await ... of,
-
Iterator
Iterator 是 ES6 引入的一種新的迭代機制,是一個統一的介面,它的作用是使各種資料結構可被便捷的訪問,它是通過一個鍵為Symbol.iterator 的方法來實作,
使用示例:
let arr = ['hello','been']
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // {value: "hello", done: false}
console.log(iterator.next()); // {value: "been", done: false}
console.log(iterator.next()); // {value: undefined, done: true}
由上我們可以看出,迭代的程序如下:
- 通過 Symbol.iterator 創建一個迭代器,指向當前資料結構的起始位置;
- 隨后通過 next 方法進行向下迭代指向下一個位置, next 方法會回傳當前位置的物件,物件包含了 value 和 done 兩個屬性, value 是當前屬性的值, done 用于判斷是否遍歷結束;
- 當 done 為 true 時則遍歷結束,
-
for...in
for...in陳述句以任意順序遍歷一個物件的除Symbol以外的可列舉屬性,
使用示例:
Object.prototype.writeBlog = function () {
console.log("hello world");
}
let obj = {
age: 22,
name: 'been'
};
for ( key in obj) {
console.log(key);
}
由于for...in會遍歷到原型上繼承來的屬性,導致輸出結果為:age name writeBlog;
當我們不需要遍歷到繼承的屬性時,可以使用hasOwnProperty() 進行判斷,如下:
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key);
}
}
// 輸出:age name
-
for...of
for...of陳述句在可迭代物件(包括 Array,Map,Set,String,TypedArray,arguments 物件等等)上創建一個迭代回圈,呼叫自定義迭代鉤子,并為每個不同屬性的值執行陳述句,ES6 新引入的迭代器,并且支持新的迭代協議,
使用示例:
let arr = ['hello','been'];
for (let val of arr) {
console.log(val);
}
//輸出:
// hello
// been
let set = new Set();
set.add('hello');
set.add('been');
for (let val of set) {
console.log(val);
}
//輸出:
// hello
// been
以上遍歷陣列和集合差不多,都是輸出屬性值,而遍歷Map物件則是輸出[key,value],如下
let map = new Map();
map.set(0, "zero");
map.set(1, "one");
for (let item of map) {
console.log(item);
}
// 輸出:
// [0,"zero"]
// [1,"one"]
for (let [key,val] of map) {
console.log(`${key}:${val}`);
}
// 輸出:
// 0:"zero"
// 1:"one"
遍歷Map物件得到的值為陣列,下標0和1分別為key和value,所以我們可以陣列解構賦值方式對應賦值,
注:for...of不能遍歷object物件,為什么呢?
let obj = {
age: 22,
name: 'been'
};
for (let key of obj) {
console.log(key);
}
// 報錯 Uncaught TypeError: obj is not iterable
因為for...of陳述句用在可迭代物件上,即需要實作迭代器Iterator,而Object物件并沒有實作這個Symbol.iterator介面,使得它無法被for...of遍歷,
那怎么讓物件可以被for...of 遍歷?缺啥就補啥咯,代碼如下:
Object.prototype[Symbol.iterator] = function*() {
let index = 0;
let arr = Object.entries(this);
let length = arr.length;
while (true) {
if (index >= length) {
return false
} else {
let key = arr[index] && arr[index][0];
let val = arr[index] && arr[index][1];
let result = { [key]: val };
index++;
yield result
}
}
};
我們給Object的原型綁了Symbol.iterator介面,用的是ES6引入的Generator函式,這時再用for...of遍歷obj時,則輸出:
-
for await ... of
for await...of 陳述句會在異步或者同步可迭代物件上創建一個迭代回圈,包括 String,Array,Array-like 物件(比如arguments 或者NodeList),TypedArray,Map, Set和自定義的異步或者同步可迭代物件,其會呼叫自定義迭代鉤子,并為每個不同屬性的值執行陳述句,像await運算式一樣,這個陳述句只能在 async function內使用,
雖然說for await ... of在異步或同步都可用,但一般用于異步的情況,同步用for ... of,
使用示例:
Symbol.asyncIterator 可指定了一個物件的默認異步迭代器,如果一個物件設定了這個屬性,它就是異步可迭代物件,可用于for await...of回圈,代碼如下:
// 創建異步可迭代物件
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 2) {
return Promise.resolve({
value: this.i++,
done: false
});
}
return Promise.resolve({
done: true
});
}
};
}
};
// 執行迭代
(async function () {
for await (num of asyncIterable) {
console.log(num);
}
})();
// 輸出:
// 0
// 1
async function * 可用來創建異步生成器,其中已經實作了異步迭代器協議, 所以可以用for await ... of迭代,代碼如下:
// 創建異步生成器
async function* asyncGenerator() {
var i = 0;
while (i < 2) {
yield i++;
}
}
// 執行迭代
(async function() {
for await (num of asyncGenerator()) {
console.log(num);
}
})();
// 輸出:
// 0
// 1
3.js中內置的迭代器(函式篇)
-
forEach()
forEach() 方法對陣列的每個元素執行一次給定的函式,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const arr = [1,2,3,4];
const res = arr.forEach((v,i,a)=>{
arr[i] = v*2;
})
console.log(arr,res);
// 輸出:[2, 4, 6, 8] undefined
我們發現,原陣列中每個元素都結果回呼函式的操作之后,都變為原來的倆倍;forEach是沒有回傳值的,
注意: 除了拋出例外以外,沒有辦法中止或跳出 forEach() 回圈,如果在forEach中使用return,只能達到continue的效果,代碼如下:
const arr = [1,2,3,4];
arr.forEach((val,i,eachArr)=>{
if(val===2){
return
}
eachArr[i] = val*2;
})
console.log(arr);
// 輸出:[2, 2, 6, 8]
由上可見,forEach()不適合需要中止或跳出回圈的情況,
-
map()
map() 方法創建一個新陣列,陣列中的元素為原始陣列元素呼叫函式處理后的值,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const arr = [1,2,3,4];
const res = arr.map(v=> v*2)
console.log(arr,res);
// 輸出:
// [1, 2, 3, 4]
// [2, 4, 6, 8]
我們發現,原陣列沒有改變,回傳的陣列為原來的倆倍;map()中修改val值,不會修改到原陣列,而是修改回傳的新陣列;
如果不使用回傳的新陣列,可以用forEach()或for...of替代,
注意: 和forEach()一樣,除了拋出例外以外,沒有辦法中止或跳出 map() 回圈,如果在map中return沒有內容時,會讓新陣列對應值為undefined,代碼如下:
const arr = [1, 2, 3, 4];
const res = arr.map(v => {
if(v === 2){
return
}
return v * 2
})
console.log(res);
// 輸出: [2, undefined, 6, 8]
-
every()
every()方法測驗一個陣列內的所有元素是否都能通過指定函式的測驗,它回傳一個布林值,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const result1 = arr.every(v=>{
console.log(v);
return v<=4
})
console.log(result1);
// 輸出:1 2 3 4 true
const result2 = arr.every(v=>{
console.log(v);
return v>2
})
console.log(result2);
// 輸出:1 false
從上面倆個輸出對比,可以發現:every 在迭代程序中,當callback回傳有false,將會立即回傳 false,不再繼續迭代,相反地,當每一個元素執行都回傳 true,every才會回傳 true,
和map()一樣,every()中操作val值,不會改變原陣列,代碼如下:
const arr = [1,2,3,4];
const res = arr.every(v=>{
v=v*2
return v>1
})
console.log(res,arr);
// 輸出:
// true [1, 2, 3, 4]
-
some()
some() 方法測驗陣列中是不是至少有1個元素通過指定函式的測驗,它回傳一個布林值,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const arr = [1,2,3,4];
const result1 = arr.some(v=>{
console.log(v);
return v<=4
})
console.log(result1);
// 輸出:
// 1 true
const result2 = arr.some(v=>{
console.log(v);
return v>4
})
console.log(result2);
// 輸出:
// 1 2 3 4 false
從上面倆個輸出對比,可以發現:some在迭代程序中,當callback回傳有true,將會立即回傳 true,不再繼續迭代,相反地,當每一個元素執行都回傳 false,some才會回傳 false,
和every()一樣,some()中操作val值,不會改變原陣列,代碼如下:
const res = arr.some((v,i,a)=>{
v=v*2
return v<2
})
console.log(res,arr);
// 輸出:
// false [1, 2, 3, 4]
-
find()
find()方法回傳陣列中滿足提供的測驗函式的第一個元素的值,否則回傳 undefined,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const arr = [5, 12, 130, 44];
const found1 = arr.find(v =>{
console.log(v);
return v > 10
});
console.log(found1);
// 輸出:5 12 12
const found2 = arr.find(v =>{
console.log(v);
return v > 130
});
console.log(found2);
// 輸出:5 12 130 44 undefined
從上面倆個輸出對比,可以發現:find在迭代程序中,當callback回傳有true,將會立即回傳 符合值,不再繼續迭代,相反地,當每一個元素執行都回傳 false,find會回傳 undefined,
-
findIndex()
findIndex()方法回傳陣列中滿足提供的測驗函式的第一個元素的索引,若沒有找到對應元素則回傳-1,
findIndex()和find()用法類似,區別:前者回傳索引,后者回傳值,所以不再贅述,
-
filter()
filter()方法創建一個新陣列, 其包含通過所提供函式實作的測驗的所有元素,
| 引數 | 描述 | ||||||||
| callback( currentValue ,index ,array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||
| thisArg | 可選,當執行回呼函式 callback 時,用作 this 的值, |
使用示例:
const arr = [5, 12, 8, 130];
const filterArr = arr.filter(v => v > 10);
console.log(filterArr,arr);
// 輸出: [12, 130] [5, 12, 8, 130]
我們發現,新陣列中的元素都為回呼函式回傳true的元素,而且原陣列不會改變,
-
reduce()
reduce() 方法對陣列中的每個元素執行回呼函式(順序從左到右),將其結果匯總為單個回傳值,
| 引數 | 描述 | ||||||||||
| callback( accumulator, currentValue, index, array ) | 必需,為陣列中每個元素執行的函式, 該函式引數:
| ||||||||||
| initialValue | 可選,初始值:作為第一次呼叫 callback函式時的accumulator值, 如果沒有提供,則使用陣列中的第一個元素, 在沒有初始值的空陣列上呼叫 reduce 將報錯, |
使用示例:
// reduce()
const arr = [1, 2, 3, 4];
let log = [];
const reducer = (accumulator, currentValue, index , arr) => {
log.push({
accumulator,
currentValue,
index ,
arr:arr.join(',')
});
return accumulator + currentValue
};
// 1 + 2 + 3 + 4 沒有初始值
const result1 = arr.reduce(reducer)
console.table(log);
console.log("回傳:",result1);
為方便查看執行程序中各引數變化,我們用console.table列印結果如下:

我們發現,當沒有初始值initialValue時,各變數會有以下規則:
- 首次執行時,accumulator為陣列第一個值,往后都是前一次執行的回傳結果;
- currentValue和index都是從第二個元素開始;
- arr原陣列不改變;
- 回傳值為最后一次呼叫回呼結果,
再來看看,在同一個回呼函式,設定初始值initialValue的情況,代碼如下:
log = [];
const result2 = arr.reduce(reducer,5);
console.table(log);
// 5 + 1 + 2 + 3 + 4 有初始值
console.log("回傳:",result2);
列印結果如下:

我們發現,當設定初始值initialValue時,各變數會有以下規則:
- 首次執行時,accumulator為初始值initialValue,往后都是前一次執行的回傳結果;
- currentValue和index都是從第一個元素開始;
- arr原陣列不改變;
- 回傳值為最后一次呼叫回呼結果,
-
reduceRight()
reduceRight() 方法對陣列中的每個元素執行回呼函式(順序從右到左),將其結果匯總為單個回傳值,
reduceRight()和reduce()用法類似,主要是執行順序不同:前者從右到左,后者從左到右,所以不再贅述,
非常感謝您的耐心閱讀,整理不易,喜歡點贊、評論、收藏、關注哦,您是最棒的!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/140391.html
標籤:其他
上一篇:Vue 3.0回應式系統編程概述
下一篇:webpack入門
