generator 是一种非凡的 iterator,generator 能够代替 iterator 实现,使代码更为简洁
什么是 iterator
iterator 叫做迭代器,是用来帮忙某个数据结构进行遍历的对象,这个对象须要合乎迭代器协定(iterator protocol)。
迭代器协定要求实现 next 办法,next 办法有如下要求
- 0 或者 1 个函数入参
- 返回值须要包含两个属性,done 和 value。
当遍历实现时,done 为 true,value 值为 undefined。
迭代器实现原理
- 创立一个指针对象,指向以后数据结构的起始地位
- 第一次调用对象的 next 办法,指针主动指向数据结构的第一个成员
- 接下来一直调用 next 办法,指针始终往后挪动,直到指向最初一个成员
- 每调用 next 办法返回一个蕴含 value 和 done 属性的对象
以下对象就实现了迭代器
const names = ["kiki", "alice", "macus"];
let index = 0;
const namesIterator = {next: () => {if (index == names.length) {return { value: undefined, done: true};
} else {return { value: names[index++], done: false };
}
},
};
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
当第四次调用 next 办法时,此时数据曾经迭代实现,所以迭代器返回 done 为 true
可迭代对象
可迭代对象与迭代器对象不同。
- 迭代器对象须要合乎迭代器协定(iterator protocol),并且返回 next 办法。
- 可迭代对象须要实现 iterable protocol 协定,即 @@iterator 办法,在代码中通过 Symbol.iterator 实现,这样当数据进行 for…of 遍历时,就用调用 @@iterator 办法。
咱们晓得,对象这种数据类型是不能够通过 for…of 对其遍历的
但如果咱们对它实现了 @@iterator 办法之后,它就变成了可迭代对象
const obj = {
name: "alice",
age: 20,
hobby: "singing",
[Symbol.iterator]: function () {
let index = 0;
const keys = Object.keys(this);
return {next: () => {if (index == keys.length) {return { value: undefined, done: true};
} else {const key = keys[index];
index++;
return {value: this[key], done: false };
}
},
};
},
};
for (let item of obj) {console.log(item);
}
const iterator = obj[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
可迭代对象的实现中包含了迭代器对象。
原生可迭代对象
简略来说,能够通过 for…of 遍历的就是可迭代对象,原生可迭代对象包含:数组、字符串、arguments、set、map
const arr = ["kiki", "alice", "macus"];
for (let item of arr) {console.log("数组", item);
}
const str = "hello";
for (let s of str) {console.log("字符串", s);
}
function foo() {for (let arg of arguments) {console.log("arguments", arg);
}
}
foo(1, 2, 3, 4);
const mapEntries = [["name", "alice"],
["age", "20"],
];
const map = new Map(mapEntries);
for (let m of map) {console.log("map", m);
}
const set = new Set(arr);
for (let s of set) {console.log("set", s);
}
以上都是原生可迭代对象
可迭代对象的用处
可迭代对象有以下用处
- for…of 遍历
- 开展语法
- 解构赋值
- 创立其余类型的对象,如 array 和 set
- Promise.all 也能够执行可迭代对象
对象能够应用开展语法和解构赋值,但它并不是可迭代对象,而是 es9 中独自实现的属性
const iteratorObj = {names: ["kiki", "alice", "macus"],
[Symbol.iterator]: function () {
let index = 0;
return {next: () => {if (index == this.names.length) {return { value: undefined, done: true};
} else {return { value: this.names[index++], done: false };
}
},
};
},
};
for (let item of iteratorObj) {console.log('for..of 遍历可迭代对象:',item);
}
const newArr = [...iteratorObj];
console.log('开展语法:',newArr);
const [name1, name2, name3] = iteratorObj;
console.log('解构赋值:',name1, name2, name3);
const set = new Set(iteratorObj);
const arr = Array.from(iteratorObj);
console.log('set:',set);
console.log('array:',arr)
Promise.all(iteratorObj).then((value) => {console.log("promise.all:", value);
});
以上办法都是获取 next 办法中的 value 值
自定义类的迭代
想要类变成可迭代对象,在类办法中增加 Symbol.iterator 办法并实现就能够了
class Student {constructor(name, age, hobbies) {
this.name = name;
this.age = age;
this.hobbies = hobbies;
}
push(hobby) {this.hobbies.push(hobby);
}
[Symbol.iterator]() {
let index = 0;
return {next: () => {if (index === this.hobbies.length) {return { done: true, value: undefined}
} else {return { value: this.hobbies[index++], done: false };
}
},
};
}
}
const student = new Student('kiki', '16', ['singing'])
student.push('swimming')
student.push('tennis')
for(let item of student){console.log(item)
}
此时能够通过 for..of 办法遍历类中的 hobbies 属性
什么是 generator
generator 叫做生成器,能够用来管制函数什么时候执行和暂停。
生成器函数也是函数,然而和一般函数之间存在如下区别
- 生成器函数之间须要增加一个 *
- 执行须要应用一个变量来接管, 每应用一次 next() 办法执行一段代码
- 通过 yield 关键字暂停函数,yield 既能够传参, 又有返回值
- 返回值是一个生成器(generator),生成器是一种非凡的迭代器
以上代码实现了生成器函数
function* foo(){console.log('开始执行')
yield
console.log('world')
yield
console.log('完结执行')
}
const generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
调用 next 办法时,返回值与迭代器统一,为蕴含 value 和 done 的对象,此时 value 为 undefine,因为 yield 后没有加上返回值
yield 传参和返回值
通过 next 办法能够将参数传递到生成器函数中,通过 yield 能够返回数据
function* foo(value1){console.log('开始执行')
const result1 = yield value1
const result2 = yield result1
const result3 = yield result2
console.log('完结执行')
}
const generator = foo('hello')
console.log(generator.next('世界'))
console.log(generator.next('merry'))
console.log(generator.next('christmas'))
console.log(generator.next('done'))
能够看到第一个 next 获取的 value 值是通过生成器函数传递的,而不是第一个 next 办法执行时的参数,所以 value 值为 ”hello” 而不是 ” 世界 ”
generator 的其余办法
- throw 办法用于抛出异样(须要在生成器函数中捕捉)
- return 办法用于中断生成器函数的执行
function* createGenerator() {console.log("开始执行");
try {
yield "hello";
console.log("hello");
} catch (error) {yield error;}
yield "world";
console.log("完结执行");
}
const generator = createGenerator();
console.log(generator.next());
console.log(generator.throw("throw"));
console.log(generator.return("return"));
应用 return 办法后,done 变为 true,value 就变成了 return 函数中传递的值
生成器代替迭代器
生成器是一种非凡的迭代器,通过生成器能够在某些场景做一些替换,使代码更为简洁
// 迭代器实现 next 办法
function createArrayIterator(arr) {
let index = 0;
return {next: function () {if (index == arr.length) {return { value: undefined, done: true};
} else {return { value: arr[index++], done: false };
}
},
};
}
// generator 遍历暂停函数
function* createArrayGenerator(arr) {for(let item of arr){yield item}
}
// yiled 语法糖
function* createArraYield(arr) {yield* arr}
const arr = ['alice', 'kiki', 'macus']
const iterator = createArrayIterator(arr)
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
const generator = createArrayGenerator(arr)
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
const yiledGen = createArraYield(arr)
console.log(yiledGen.next())
console.log(yiledGen.next())
console.log(yiledGen.next())
console.log(yiledGen.next())
以上三种形式所实现的性能是统一的
类中应用生成器
类中遍历的形式由迭代器改为生成器
class Student {constructor(name, age, hobbies) {
this.name = name;
this.age = age;
this.hobbies = hobbies;
}
push(hobby) {this.hobbies.push(hobby);
}
*[Symbol.iterator]() {yield* this.hobbies}
}
const student = new Student('kiki', '16', ['singing'])
student.push('swimming')
student.push('tennis')
for(let item of student){console.log(item)
}
以上就是 iterator 和 generator 的用法和分割,对于 js 高级,还有很多须要开发者把握的中央,能够看看我写的其余博文,继续更新中~