簡單來講,迭代器(iterator)就是一個知足必定規則的對象。數組
規則:若是一個對象中含有一個next
方法,next
方法中返回一個對象,其中中包含兩個屬性value
、done
,那麼這個對象就是一個迭代器。瀏覽器
以下代碼就是一個迭代器:dom
//生成隨機數的迭代器 let iterator = { total: 2, i: 1, next(){ let obj = { value: this.i > this.total ? undefined : Math.random(), //下一個數據的值 done: this.i > this.total, //是否還有下一個數據 } this.i++; return obj; } }
執行方式,調用next
函數異步
console.log(iterator.next()) //{value: 1, done: false} console.log(iterator.next()) //{value: 2, done: false} console.log(iterator.next()) //{value: undefined, done: true}
遍歷迭代器數據函數
let next = iterator.next(); while(!next.done){ console.log(next.value); next = iterator.next(); }
一個返回迭代器對象的函數。this
function createIterator(total = 2){ let i = 1; return { next(){ let obj = { value: i > total ? undefined : Math.random(), //下一個數據的值 done: i > total, //是否還有下一個數據 } i++; return obj; } } } const iterator = createIterator(5)
將一個數組改爲迭代模式:code
function createArrIterator(arr){ let i = 0; return { next(){ let obj = { value: arr[i++], done: i > arr.length } return obj; } } } const iterator = createArrIterator([3, 4,5, 23, 4, 6, 34]);
只有知足迭代協議的對象才能使用for of
遍歷,不然就會報錯。對象
const obj = {} for(const i of obj){ console.log(i) // "Uncaught TypeError: obj is not iterable" }
若是想使用for of
遍歷obj
,只要將obj
改形成知足可迭代協議的對象便可。開發
迭代協議:知足可迭代協議的對象要有一個符號屬性(Symbol.iterator)
,屬性的值是一個無參的迭代器建立函數。以下所示:get
//obj加入[Symbol.iterator]屬性後可被迭代 const obj = { [Symbol.iterator]: (total = 2) => { //迭代器建立函數 let i = 1; return { next() { let obj = { value: i > total ? undefined : Math.random(), //下一個數據的值 done: i > total, //是否還有下一個數據 } i++; return obj; } } } } for(const i of obj){ console.log(i); // 不報錯 }
Array
、Map
、Set
可使用for of
遍歷,是由於他們的原型上都有符號屬性(Symbol.iterator)
實際上for of
遍歷可迭代對象時,就是運行該對象上的Symbol.iterator
屬性,而後調用迭代器的next
方法直到done
屬性是true
位置。
const obj = { [Symbol.iterator]: (total = 2) => { let i = 1; return { next() { let obj = { value: i > total ? undefined : Math.random(), //下一個數據的值 done: i > total, //是否還有下一個數據 } i++; return obj; } } } } for(const i of obj){ console.log(i); // 不報錯, i實際上就是迭代器next方法返回對象裏面的value屬性 } //for of 代碼至關於以下代碼: const iterator = obj[Symbol.iterator](); let item = iterator.next(); while(!item.done){ const i = item.value; console.log(i) //執行for of中的代碼 item = iterator.next() }
由構造函數Generator
建立的對象,建立的對象是一個迭代器,也知足可迭代協議。
Generator構造函數是瀏覽器內置的,開發者沒法使用。
//僞代碼 const generator = new Generator(); //generator的內部大概是這樣的: generator = { next(){...}, //具備next函數 [Symbol.iterator](){...}, //具備(Symbol.iterator)屬性 }
開發者沒法直接調用Generator
建立生成器,只能使用生成器建立函數建立一個生成器。
生成器函數定義:只要function
關鍵字和函數名之間加入一個*
,該函數就是生成器函數,會返回一個生成器。
function *createGenerator(){} const generator = createGenerator(); //獲得一個生成器 console.log("next" in generator); //true console.log(Symbol.iterator in generator); //true console.log(generator.next === generator[Symbol.iterator]().next); //true
yield
關鍵字寫在生成器內部,至關於暫停,配合next
方法能夠在生成器外部控制生成器函數內部代碼的執行。
yield
關鍵字只能在生成器內部使用;next
方法後,代碼會從上一個yield
執行到下一個yield
;yield
關鍵字,則裏面的代碼一行都不會執行;return
返回的值做爲next
方法的done
第一次爲true
時的value
值;yield
關鍵字後面的值會當作執行next
方法時返回的value
值;yield
關鍵字表達式返回的值等於執行next
方法時傳入的參數。舉個例子:你們最好使用瀏覽器,手動調用generator.next()
,一步一步看比較容易理解。
function* createGenerator() { let res; console.log("開始", res); res = yield 1; //將第2個next傳入的參數做爲返回值賦值給res console.log("打印1", res); res = yield 2; //將第3個next傳入的參數做爲返回值賦值給res console.log("打印2", res); return "結束" } const generator = createGenerator(); let result = generator.next(); console.log("調用第1次next的返回值:", result); result = generator.next(result.value); console.log("調用第2次next的返回值:", result); result = generator.next(result.value); console.log("調用第3次next的返回值:", result); result = generator.next(result.value); console.log("調用第4次next的返回值:", result); //輸出: 開始 undefined 調用第1次next: { value: 1, done: false } 打印1 1 調用第2次next: { value: 2, done: false } 打印2 2 調用第3次next: { value: '結束', done: true } 調用第4次next: { value: undefined, done: true }
/** * 模擬一個請求 */ function getData() { return new Promise(resolve => { setTimeout(() => { resolve('數據'); }, 2000) }) } /** * 生成器函數,在這裏寫業務邏輯,能夠將異步代碼的寫法轉化爲同步代碼寫法 */ function* task() { console.log('獲取數據中...'); let result = yield getData(); //將異步代碼轉化爲同步的寫法 console.log('獲得數據:', result); //對數據進行後續處理... } /** * 運行生成器的通用函數 */ function run(generatorFunc) { const generator = generatorFunc(); next(); function next(nextValue) { let result = generator.next(nextValue) if (result.done) { //迭代結束 return; } else { const value = result.value; if (value instanceof Promise) { value.then(data => next(data)); } else { next(value); } } } } run(task); //執行生成器函數代碼 //輸出: 獲取數據中... 獲得數據: 數據