迭代器(iterator)是一個結構化的模式,用於從源以一次一個的方式提取數據。迭代器的使用能夠極大地簡化數據操做,因而ES6也向JS中添加了這個迭代器特性。新的數組方法和新的集合類型(如Set集合與Map集合)都依賴迭代器的實現,這個新特性對於高效的數據處理而言是不可或缺的,在語言的其餘特性中也都有迭代器的身影:新的for-of循環、展開運算符(...),甚至連異步編程均可以使用迭代器。前端
今天筆者將從如下幾個方面進行介紹迭代器:數據庫
本篇文章閱讀時間預計6分鐘。編程
迭代器是一種有序、連續的、基於拉取的用於消耗數據的組織方式,用於以一次一步的方式控制行爲。簡單的來講咱們迭代循環一個可迭代對象,不是一次返回全部數據,而是調用相關方法分次進行返回。數組
迭代器iterator是一個object,這個object有一個next函數,該函數返回一個有value和done屬性的object,其中value指向迭代序列中當前next函數定義的值。bash
ES6的迭代協議分爲迭代器協議(iterator protocol)和可迭代協議(iterable protocol),迭代器基於這兩個協議進行實現。微信
迭代器協議: iterator協議定義了產生value序列的一種標準方法。只要實現符合要求的next函數,該對象就是一個迭代器。至關遍歷數據結構元素的指針,相似數據庫中的遊標。數據結構
可迭代協議: 一旦支持可迭代協議,意味着該對象能夠用for-of來遍歷,能夠用來定義或者定製JS 對象的迭代行爲。常見的內建類型好比Array & Map都是支持可迭代協議的。對象必須實現@@iterator方法,意味着對象必須有一個帶有@@iterator key的能夠經過常量Symbol.iterator訪問到的屬性。閉包
下圖展現了arrays,Maps,Strings數據類型實現了可迭代協議,咱們可使用for-of和展開語法顯示迭代器的數據。異步
以下代碼展現了基於迭代協議進行實現:異步編程
let obj = {
array: [1, 2, 3, 4, 5],
nextIndex: 0,
next: function() {
return this.nextIndex < this.array.length ?
{value: this.array[this.nextIndex++], done: false} :
{done: true}
}
};
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().done);複製代碼
上述代碼將會輸出
1
2
3
4
5
true複製代碼
上述代碼的next方法還能夠按以下代碼進行改寫,看起來更清晰些:
if(this.nextIndex < this.array.length) {
this.nextIndex++;
return { value: this.array[this.nextIndex], done: false }
} else {
return { done: true }
}複製代碼
咱們能夠看出,next方法的實現,若是存在新的元素,返回當前元素的並將當前元素位置的標識遞增長1,當沒有元素時,返回{ done: true }。
根據可迭代協議,對象須要提供@@iterator方法; 也就是說,它必須將Symbol.iterator符號做爲屬性鍵。 @@iterator方法必須返回迭代器對象。代碼實現以下:
let obj = {
array: [1, 2, 3, 4, 5],
nextIndex: 0,
[Symbol.iterator]: function(){
return {
array: this.array,
nextIndex: this.nextIndex,
next: function(){
return this.nextIndex < this.array.length ?
{value: this.array[this.nextIndex++], done: false} :
{done: true};
}
}
}
};
let iterable = obj[Symbol.iterator]()
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().done);複製代碼
上述代碼將會輸出
1
2
3
4
5
true複製代碼
上述代碼,咱們實現了自定義的迭代器,基於JS的做用域和閉包特性才能輕鬆實現。arrays,Maps,Strings數據類型實現了可迭代協議,其 __proto__原型鏈指向自帶Symbol.iterator的方法,節省了咱們手寫代碼的時間,以下代碼所示:
const arr = [1, 2];
const iterator = arr[Symbol.iterator](); // returns you an iterator
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())複製代碼
上述代碼將會輸出:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }複製代碼
咱們可使用for-of,展開語法迭代數組,示例代碼以下:
const arr = [1, 2];
for(var v of arr){
console.log(v);
}
//outputs 1
//outputs 2
console.log([...arr]);
//outputs[1,2];複製代碼
obj對象沒有實現可迭代協議,咱們如何迭代obj對象呢?實現obj的迭代器呢,示例代碼以下:
var obj={
a:1,
b:2,
c:3,
[Symbol.iterator]:function () {
var keys=Object.keys(this);//object.vulues(this)
var index=0;
return{
next:()=>
(index<keys.length)?
{value: this[keys[index++]], done:false} :
{done: true,value:undefined}
}
}
};
console.log([...obj]);
//outputs [1,2,3]複製代碼
斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲「兔子數列」,指的是這樣一個數列:一、一、二、三、五、八、1三、2一、3四、……在數學上,斐波納契數列以以下被以遞推的方法定義:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)。
咱們可使用迭代器來產生一個序列數不大於100的斐波那契數列,示例代碼以下:
let Fib= {
[Symbol.iterator](){
let n1=1;
let n2=1;
let max=100;
return {
next(){
let current=n2;
n2=n1;
n1=n1+current;
if(current<max){
return {value: current, done: false}
}else{
return { done: true}
}
}
}
}
}
console.log([...Fib]);
//outputs [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]複製代碼
有時候咱們須要要執行任務放在一個隊列裏,分次執行,咱們可使用迭代器進行模擬,示例代碼以下:
let tasks={
actions:[],
[Symbol.iterator](){
let steps=this.actions.slice();
return {
[Symbol.iterator]() {return this;},
next(...args){
if(steps.length>0){
let res=steps.shift()(...args);
return {value:res,done:false};
}
else{
return {done:true}
}
},
return(v){
steps.length=0;
return {value:v,done:true};
}
};
}
};
tasks.actions.push(
function step1(x) {
console.log("step 1:",x);
return x*2;
},
function step2(x,y) {
console.log("step 2:",x,y);
return x+(y*2);
},
function step3(x,y,z) {
console.log("step 3:",x,y,z);
return (x*y)+z;
}
);
let it=tasks[Symbol.iterator]();
console.log(it.next(10));
console.log(it.next(20,50));
console.log(it.next(20,50,120));
console.log(it.next());複製代碼
上述代碼輸出:
step 1: 10
{ value: 20, done: false }
step 2: 20 50
{ value: 120, done: false }
step 3: 20 50 120
{ value: 1120, done: false }
{ done: true }複製代碼
從上述代碼,咱們能夠看出,迭代器不單單是數據的迭代,還能夠做爲一個模式來組織相關的功能。(注:本示例來源《你不知道的JavaScript》下卷)
今天的內容就到這裏,迭代器是否是很神奇,好像如魔法通常,咱們隨意控制函數的中斷與繼續,豐富了咱們解決問題的思路,讓咱們的代碼看起來更加工程化和結構化,提升了代碼的可讀性和可理解性。
更多精彩內容,請微信關注」前端達人」公衆號!