迭代器和生成器

shuxiaotai發表於2019-02-11

一.迭代器

1.迭代器是帶有特殊介面的物件,返回一個next方法,該方法中同時又valuedone屬性,當再沒有值可以迭代時,valueundefineddonetrue,否則value為當前值,donefalse

2.根據上面的描述實現一個迭代器,如下:

let iterator = {
	i: 0,
	items: [2, 4, 6],
	next() {
		let value, done;
		done = (this.i === this.items.length);
		value = done ? undefined : this.items[this.i++];
		return {
			value: value,
			done: done
		}
	}
};
console.log(iterator.next());  //{value: 2, done: false}
console.log(iterator.next());  //{value: 4, done: false}
console.log(iterator.next());  //{value: 6, done: false}
console.log(iterator.next());  //{value: undefined, done: true}
複製程式碼

二.生成器

1.生成器是返回迭代器的函式,如下:

function *createIterator() {
	yield 1;
	yield 2;
	yield 3;
}
let iterator = createIterator();
console.log(iterator.next());  //{value: 1, done: false}
console.log(iterator.next());  //{value: 2, done: false}
console.log(iterator.next());  //{value: 3, done: false}
console.log(iterator.next());  //{value: undefined, done: true}
複製程式碼

2.生成器有幾點要注意的:

  • 當執行流遇到yield語句時,該生成器就停止運轉了,直到迭代器再次呼叫next
  • 可以再for迴圈中使用yield
  • yield只能用在生成器的內部,即使是生成器內部的函式也不行,即:yield無法跨越函式邊界
  • 無法使用箭頭函式建立生成器
  • 生成器可以存在於物件的屬性中

三.for-of迴圈

1.可迭代型別:指那些包含Symbol.iterator屬性的物件,該屬性定義了返回迭代器的函式(如:陣列,set,map等)

2.for-of迴圈可以迴圈可迭代型別,for-of迴圈會在可迭代型別每次執行後呼叫next()並將結果儲存在變數中,迴圈會持續進行,直到結果物件的done屬性為true

3.for-of迴圈會呼叫陣列的Symbol.iterator屬性來獲取迭代器(該方法由幕後的js引擎呼叫),並將呼叫iterator.next(),並將該結果物件的value屬性的值賦給num,直到done為true,迴圈會退出,num不會被賦給undefined,程式碼如下:

let values = [1, 2, 3];
for (let item of values) {
	console.log(item);   //1 2 3
}
複製程式碼

4.對於非可迭代物件,如nullundefined,使用for-of迴圈會丟擲錯誤

5.可以在for-of迴圈中使用解構

let map = new Map();
map.set(`name`, `sxt`);
map.set(`age`, 2);
for(let [key, value] of map) {
	console.log(key + " = " + value);
}
//輸出:
//name = sxt
//age = 2
複製程式碼

6.for-of迴圈可以用於迴圈NodeList

四.Symbol.iterator

1.可以用Symbol.iterator屬性來訪問物件預設的迭代器,如:

let arr = [6, 7, 8];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());  //{value: 6, done: false}
console.log(iterator.next());  //{value: 7, done: false}
console.log(iterator.next());  //{value: 8, done: false}
console.log(iterator.next());  //{value: undefined, done: true}
複製程式碼

2.判斷一個物件是否可以迭代,可以通過判斷Symbol.iterator屬性是否是一個函式來實現,如:

function isIterator(obj) {
	return typeof obj[Symbol.iterator] === `function`;
}

let arr = [1, 3, 4];
let num = 1;
console.log(isIterator(arr)); //true
console.log(isIterator(num)); //false
複製程式碼

3.建立可迭代型別: 我們自己定義的物件預設是不可迭代型別,但是我們可以通過設定Symbol.iterator屬性來使這個物件可以迭代。因為前面第3點有講到,判斷一個物件是否可以迭代,其實是通過Symbol.iterator屬性來確定的,由此可以建立下面的物件

let obj = {
	*[Symbol.iterator]() {
		yield 1;
		yield 2;
		yield 3;
	}
};
for (let item of obj) {
	console.log(item);  //1 2 3
}
複製程式碼

五.內建迭代器

1.我們平常迭代陣列,set和map時,能拿到迭代的值,是因為這些集合中有內建的迭代器,如

let arr = [1, 3, 5];
let set = new Set();
set.add(`time`);
set.add(`user`);
set.add(`family`);
let map = new Map();
map.set(`name`, `sxt`);
map.set(`age`, 12);
map.set(`sister`, `andy`);

for(let item of arr) {
	console.log(item);  // 1 3 5
}
for(let item of set) {
	console.log(item); //time user family
}
for(let item of map) {
	console.log(item); //["name", "sxt"]  ["age", 12] ["sister", "andy"]
}
複製程式碼

2.內建迭代器分為三種

  • entries()
  • keys()
  • values()

3.陣列的entries返回的是[索引,值],set的entries是[值,值],因為set的鍵值是一樣的,map的entries是[鍵名,鍵值],如:

let arr = [1, 3, 5];
let set = new Set();
set.add(`time`);
set.add(`user`);
set.add(`family`);
let map = new Map();
map.set(`name`, `sxt`);
map.set(`age`, 12);
map.set(`sister`, `andy`);

for(let item of arr.entries()) {
	console.log(item);  // [0, 1]  [1, 3]  [2, 5]
}
for(let item of set.entries()) {
	console.log(item); //["time", "time"]  ["user", "user"] ["family", "family"]
}
for(let item of map.entries()) {
	console.log(item); //["name", "sxt"]  ["age", 12] ["sister", "andy"]
}
複製程式碼

4.陣列的keys返回的索引,set的keys返回的還是值,map的keys返回的是值
5.陣列,set,map的values返回的都是值

六.向迭代器中傳遞引數

1.我們可以向迭代器中傳遞引數

function *createIterator() {
	let first = yield 1;
	let second = yield first + 2;
	yield second + 3;
}

let iterator = createIterator();
console.log(iterator.next());   //{value: 1, done: false}
console.log(iterator.next(4));  //{value: 6, done: false}
console.log(iterator.next(5));  //{value: 8, done: false}
console.log(iterator.next());   //{value: undefined, done: true}
複製程式碼
  • 這裡的難點是理解右側的程式碼會和左邊的中斷
  • 首次呼叫next的時候,不管傳入什麼引數都會被忽略,因為傳入的引數會作為yield語句的返回值,而第一次只是yield 1,而沒有變數

七.包含return語句的生成器

function *createIterator() {
	yield 1;
	return 133;
	yield 2;
	yield 3;
}

let iterator = createIterator();
console.log(iterator.next());   //{value: 1, done: false}
console.log(iterator.next(4));  //{value: 133, done: true}
console.log(iterator.next(5));  //{value: undefined, done: true}
console.log(iterator.next());   //{value: undefined, done: true}

複製程式碼
  • return會讓它提前執行完畢並針對next的呼叫返回一個值

八.生成器代理

1.即,在一個生成器中呼叫另外的生成器,有時候,這樣的操作會更加的實用

function *create1() {
	yield 1;
	yield 2;
	yield 3;
}

function *create() {
	yield *create1();
	yield true;
}


let iterator = create();
console.log(iterator.next());  //{value: 1, done: false}
console.log(iterator.next());  //{value: 2, done: false}
console.log(iterator.next());  //{value: 3, done: false}
console.log(iterator.next());  //{value: true, done: false}
console.log(iterator.next());  //{value: undefined, done: true}
複製程式碼

相關文章