什么是迭代器
-
迭代器是确使用户可在容器对象(container,例如链表或数组)上
遍访
的对象,使用该接口无需关心对象的内部实现细节 - 迭代器是帮助我们对某个数据结构进行遍历的对象。
- 在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合
迭代器协议
(iterator protocol
)- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式;
- 那么在js中这个标准就是一个特定的
next方法
;
- next方法有如下要求:
- 一个
无参函数
或有一个参数的函数
,返回一个应当拥有以下两个属性的对象
: -
done(boolean)
- 如果迭代器可以产生序列中的下一个值,则为false.
- 如果迭代器已将序列迭代完毕,则为true.这种情况下,value是可选的,如果它依然存在,即为迭代结束后的默认返回值。
-
value
- 迭代器返回的任何JavaScript值。done为true时,value可省略。
- 一个
1.编写一个迭代器:
迭代器是一个对象
迭代器实现了特定的next方法
- next方法是一个无参函数或有一个参数的函数
- 返回值为一个对象,对象有done和value两个属性
const iterator = {
next() {
return {
done: false,
value: "aaa",
};
},
};
// 调用迭代器的next方法,返回一个对象
console.log(iterator.next()); // { done: false, value: 'aaa' }
迭代器可以帮助我们对某个数据结构进行遍访
而上面并没有实现遍访某个数据结构
我们需要实现,在调用迭代器的next方法时,可以依次访问某个数据结构中的元素
const names = ["lily", "koby", "curry", "coderwhy"];
let index = 0;
const namesIterator = {
next() {
if (index < names.length) {
return { done: false, value: names[index++] };
} else {
return { done: true }; // done为true时,value可省略
// return {done: true, value: undefined} 返回对象的value可以为undefined
// return {done: true, value: '默认值'} 返回对象的value可以为任意默认值
}
},
};
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: false, value: 'curry' }
console.log(namesIterator.next()); // { done: false, value: 'coderwhy' }
console.log(namesIterator.next()); // { done: true }
console.log(namesIterator.next()); // { done: true }
2.封装一个可以生成迭代器的函数:
/**
* 封装一个可为数组产生迭代器的函数
* 返回一个迭代器对象
*/
function createArrayIterator(arr) {
let index = 0;
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
},
};
}
const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }
const nums = [1, 3, 4];
const numsIterator = createArrayIterator(nums);
console.log(numsIterator.next()); // { done: false, value: 1 }
console.log(numsIterator.next()); // { done: false, value: 3 }
console.log(numsIterator.next()); // { done: false, value: 4 }
console.log(numsIterator.next()); // { done: true, value: undefined }
/**
* 创建一个无限的迭代器
*/
function createNumbersIterator() {
let index = 0;
return {
next() {
return { done: false, value: index++ };
},
};
}
const numbersIterator = createNumbersIterator();
console.log(numbersIterator.next()); // { done: false, value: 0 }
console.log(numbersIterator.next()); // { done: false, value: 1 }
console.log(numbersIterator.next()); // { done: false, value: 2 }
console.log(numbersIterator.next()); // { done: false, value: 3 }
2.可迭代对象
当一个对象实现了iterable protocol
协议时,它就是一个可迭代对象
这个对象要求必须实现@@iterator方法,在代码中我们使用Symbol.iterator访问该属性
@@iterator方法执行必须返回一个迭代器
/**
* 可迭代对象:
* 是一个对象
* 有一个Symbol.iterator属性,属性值为一个函数,执行函数会返回一个迭代器
*/
// 创建一个可迭代对象
const iterableObj = {
values: ["koby", "lily"],
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
if (index < this.values.length) {
return { done: false, value: this.values[index++] };
} else {
return { done: true, value: undefined };
}
},
};
},
};
// 调用可迭代对象的Symbol.iterator方法可获取生成器
const iterator = iterableObj[Symbol.iterator]();
//执行生成器的next方法,可以遍访某个数据结构
console.log(iterator.next()); // { done: false, value: 'koby' }
console.log(iterator.next()); // { done: false, value: 'lily' }
console.log(iterator.next()); // { done: true, value: undefined }
for of可以遍历的东西必须必须是一个可迭代对象
- 1.它会通过调用对象的Symbol.iterator方法获取迭代器iterator
- 2.通过执行迭代器的next方法去访问目标容器中的元素,取到next方法返回的对象的value值,赋值给item
- 3.当next方法返回对象的done值为true,结束遍历目标
下面示例,for of遍历了一个不可迭代对象,结果报错
const obj = {
name: "why",
age: 18,
};
//for of 遍历不同对象会报错,因为普通对象不是一个可迭代对象:TypeError: obj is not iterable
for (const item of obj) {
console.log(item);
}
下面示例,for of遍历了一个可迭代对象
const iterableObj = {
values: ["koby", "lily"],
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
if (index < this.values.length) {
return { done: false, value: this.values[index++] };
} else {
return { done: true, value: undefined };
}
},
};
},
};
for (const item of iterableObj) {
console.log(item)
}
输出:
koby
lily
原生可迭代对象
事实上,我们平时创建的很多原生对象已经实现了可迭代协议,本身就是一个可迭代对象:
- String、Array、Map、Set、arguments对象,NodeList对象
/**
* String、Array、Map、Set、arguments对象,NodeList对象本身就是可迭代对象
*/
// 字符串对象是可迭代对象
const str = "abc";
console.log(str[Symbol.iterator]); // [Function: [Symbol.iterator]]
for (const s of str) {
console.log(s);
}
// a
// b
// c
// 数组对象是可迭代对象
const arr = ["lily", "koby"];
console.log(arr[Symbol.iterator]); // [Function: values]
for (const a of arr) {
console.log(a);
}
// lily
// koby
// Set对象是可迭代对象
const set = new Set();
console.log(set[Symbol.iterator]); // [Function: values]
set.add(1);
set.add(2);
for (const item of set) {
console.log(item);
}
// 1
// 2
// Map对象是可迭代对象
const map = new Map();
console.log(map[Symbol.iterator]); // [Function: entries]
map.set(0, "你");
map.set(1, "好");
for (const m of map) {
console.log(m);
}
// [ 0, '你' ]
// [ 1, '好' ]
// arguments是可迭代对象
function sum(a, b) {
console.log(arguments[Symbol.iterator]); // [Function: values]
for (const a of arguments) {
console.log(a);
}
return a + b;
}
sum(5, 6);
// 5
// 6
可迭代对象的用法
可迭代对象可用在:
- JavaScript语法:for...of、展开语法、yield*、解构赋值;
- 创建一些对象时:new Map([iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]);
- 一些方法的调用:Promise.all(iterable)、Promise.all(iterable)、Array.from(iterable)
/**
* 可迭代对象用法:
* for of
* 展开运算符...
* 解构
* 创建一些其他对象:作为new Set()的参数,Array.from()的参数
*/
const iterableObj = {
values: ["lily", "koby"],
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
if (index < this.values.length) {
return { done: false, value: this.values[index++] };
} else {
return { done: true };
}
},
};
},
};
// 展开运算符
const arr = ["abc", "cde"];
const newArr = [...arr, ...iterableObj];
console.log(newArr); //[ 'abc', 'cde', 'lily', 'koby' ]
// 对象虽然不是可迭代对象,但依然可以使用展开运算符展开,
// 这是ES9(ES2018)新增的特性,内部对对象做了特殊处理,用的不是迭代器
const obj = { name: "why", age: 18 };
const newObj = { ...obj };
console.log(newObj); //{ name: 'why', age: 18 }
// 解构
const [str1, str2] = arr;
console.log(str1, str2); //abc cde
//对象的解构依然是ES9新增语法,内部对对象做了特殊处理,用的不是迭代器
const { name, age } = obj;
console.log(name, age); // why 18
// 创建一些其他对象:作为new Set()的参数,Array.from()的参数
//1.把数组转换为Set对象
const set1 = new Set(iterableObj);
const set2 = new Set(arr);
console.log(set1); // Set(2) { 'lily', 'koby' }
console.log(set2); // Set(2) { 'abc', 'cde' }
//2.把类数组arguments转换为数组
function sum(a, b) {
const args = Array.from(arguments);
console.log(args); // [ 1, 2 ]
return a + b;
}
sum(1, 2);
// Promise.all和Promise.race的参数为一个可迭代对象
Promise.all(iterableObj).then((res) => {
console.log(res); // [ 'lily', 'koby' ]
});
自定义类的可迭代性
案例:创建一个Classroom类
- 教室中有自己的位置,名称,当前教室的学生
- 这个教室可以进来新学生(add)
- 创建的教室对象都是可迭代对象
/**
* 案例:创建一个Classroom类
* 教室中有自己的位置,名称,当前教室的学生
* 这个教室可以进来新学生(add)
* 创建的教室对象都是可迭代对象
*/
class Classroom {
constructor(name, address, students) {
this.name = name;
this.address = address;
this.students = students;
}
add(stu) {
this.students.push(stu);
}
[Symbol.iterator] = function () {
let index = 0;
return {
next: () => {
if (index < this.students.length) {
return { done: false, value: this.students[index++] };
} else {
return { done: true };
}
},
// 监听迭代器被提前终止,正常终止监听不到
return: () => {
console.log("监听到迭代器终止了");
return { done: true };
},
};
};
}
const classroom = new Classroom("测控技术与仪器", "226", [
"lily",
"koby",
"curry",
]);
for (const stu of classroom) {
console.log(stu);
if (stu === "koby") break;
}
// lily
// koby
// 监听到迭代器终止了
**迭代器在某些情况下会在没有完全迭代的情况下中断:
- 比如遍历的过程中通过break、continue、return、throw中断了循环操作
- 比如在解构的时候,没有完全解构
那么这个时候我们想要监听中断的话,可以添加return方法
非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript
的知识
网友评论