前端设计模式之迭代器模式(六)
- 统一遍历的模式
- 用于顺序访问集合对象的元素,不需要知道集合对象的底层表示
- ES6 之后使用迭代器(Iterator),任何结构只要具备
Symbol.iterator
属性,就可以被for...of
循环和迭代器的next
方法遍历 for...of
的背后正是对next
方法的反复调用- 简单的 for 循环并不是迭代器模式,因为 for 循环需要知道对象的内部结构(长度和项)
- forEach 就是最简易的迭代器
-
- 不需要知道长度
- 不需要知道如何获取 item
const pList = document.querySelectorAll("p");
pList.forEach(p => console.log(p));
实现迭代器模式
class DataIterator {
private data: number[];
private index = 0;
constructor(container: DataContainer) {
this.data = container.data;
}
next(): number | null {
if (this.hasNext()) {
return this.data[this.index++];
}
return null;
}
hasNext() {
if (this.index >= this.data.length) return false;
return true;
}
}
class DataContainer {
data: number[] = [10, 20, 30, 40];
getIterator() {
return new DataIterator(this);
}
}
const container = new DataContainer();
const iterator = container.getIterator();
while (iterator.hasNext()) {
const num = iterator.next();
console.log(num);
}
是否符合设计原则?
- 使用者和目标数据分离,解耦
- 目标数据自行控制内部迭代逻辑
- 使用者不关心目标数据的内部结构
场景
JS 有序对象,都内置迭代器
- 数组
- 字符串
- NodeList 等 DOM 集合
- arguments
- Set
- Map
Symbol.iterator
- 每个有序对象,都内置了
Symbol.iterator
属性,属性值是一个函数 - 执行该函数讲返回
iterator
迭代器,有next()
方法,执行返回{ value, done }
结构
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
iterator.next(); // {value: 10, done: false}
iterator.next(); // {value: 20, done: false}
iterator.next(); // {value: 30, done: false}
iterator.next(); // {value: undefined, done: true}
另外,有些对象的 API 也会生成有序对象
const map = new Map([
["k1", "v1"],
["k2", "v2"],
]);
const mapIterator = map[Symbol.iterator]();
const values = map.values();
const valuesIterator = values[Symbol.iterator]();
自定义迭代器
interface IteratorRes {
value: number | undefined;
done: boolean;
}
class CustomIterator {
private length = 3;
private index = 0;
next(): IteratorRes {
this.index++;
if (this.index <= this.length) {
return { value: this.index, done: false };
}
return { value: undefined, done: true };
}
[Symbol.iterator]() {
return this;
}
}
const iterator = new CustomIterator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
函数:
// 定义生成器函数,入参是任意集合
function iteratorGenerator(list) {
// idx记录当前访问的索引
let idx = 0;
// len记录传入集合的长度
const len = list.length;
return {
// 自定义next方法
next: function () {
// 如果索引还没有超出集合长度,done为false
const done = idx >= len;
// 如果done为false,则可以继续取值
const value = !done ? list[idx++] : undefined;
// 将当前值与遍历是否完毕(done)返回
return {
done: done,
value: value,
};
},
};
}
const iterator = iteratorGenerator(["1号选手", "2号选手", "3号选手"]);
console.log(iterator.next()); // { done: false, value: '1号选手' }
console.log(iterator.next()); // { done: false, value: '2号选手' }
console.log(iterator.next()); // { done: false, value: '3号选手' }
console.log(iterator.next()); // { done: true, value: undefined }
有序结构的作用
for...of
- 所有有序结构,都支持 for...of 语法
支持某些数组操作
- 数组解构
const [node1, node2] = someDomList
- 扩展操作符
const arr = [...someDomList]
- Array.from()
const arr = Array.form(someDomList)
- 创建 Map 和 Set
const map = new Map([
['k1', 'v1'],
['k2', 'v2']
])
const set = new Set(someDomList)
- Promise.all 和 Promise.race
Promise.all([promise1, promise2, promise3])
Promise.race([promise1, promise2, promise3])
Generator
- 迭代器和生成器,两者密不可分
基本使用
function* genNums() {
yield 10;
yield 20;
yield 30;
}
const numsIterator = genNums();
numsIterator.next(); // {value: 10, done: false}
numsIterator.next(); // {value: 20, done: false}
numsIterator.next(); // {value: 30, done: false}
numsIterator.next(); // {value: undefined, done: true}
// for (let n of numsIterator) {
// console.log(n)
// }
yield* 语法
- 有序结构可用于
yield*
function* genNums() {
yield* [100, 200, 300]; // 相当于:循环数组,分别 yield
}
const numsIterator = genNums();
numsIterator.next(); // {value: 100, done: false}
numsIterator.next(); // {value: 200, done: false}
numsIterator.next(); // {value: 300, done: false}
numsIterator.next(); // {value: undefined, done: true}
// for (let n of numsIterator) {
// console.log(n)
// }
最简单的自定义迭代器
class CustomIterator {
private data: number[];
constructor() {
this.data = [10, 20, 30];
}
*[Symbol.iterator]() {
yield* this.data;
}
}
const iterator = new CustomIterator();
for (let n of iterator) {
console.log(n);
}
yield 遍历 DOM 树
function* traverse(elemList: Element[]): any {
for (const elem of elemList) {
yield elem;
const children = Array.from(elem.children);
if (children.length) {
yield* traverse(children);
}
}
}
const container = document.getElementById("container");
if (container) {
for (let node of traverse([container])) {
console.log(node);
}
}
转载自:https://juejin.cn/post/7197424168622964791