「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。
大家好,我是L同学。本篇文章主要总结了迭代器和可迭代对象的知识点。
什么是迭代器
迭代器是一个对象,并且需要符合迭代器协议,即这个对象需要有特定的next方法。迭代器可以帮助我们遍历某种数据结构。
next方法
next方法会返回一个对象,这个对象具有两个属性: done(boolean)属性和value属性。
const iterator = {
next: function () {
return {
done: true,
value: 123
}
}
}
下面来具体介绍这两个属性。
done(boolean):
- 如果迭代器可以产生序列中的下一个值,则为false,这等价于没有指定done这个属性。
- 如果迭代器已经将序列迭代完毕,则为true。这种情况下,value是可选的,如果它依然存在,即为迭代结束之后的默认返回值。
value:
迭代器返回的任何javascript值,done为true时可忽略。
下面我们创建一个迭代器来遍历数组.
const names = ['abc', 'cba', 'nba']
let index = 0
const namesIterator = {
next: function () {
if (index < names.length) {
return { done: false, value: names[index++] }
} else {
return { done: true, value: undefined }
}
}
}
console.log(namesIterator.next()); // { done: false, value: 'abc' }
console.log(namesIterator.next()); // { done: false, value: 'cba' }
console.log(namesIterator.next()); // { done: false, value: 'nba' }
console.log(namesIterator.next()); // { done: true, value: undefined }
console.log(namesIterator.next()); // { done: true, value: undefined }
以上生成的迭代器是只针对names数组的,不具有通用性,下面我们来对这个迭代器进行封装。
function createArrayIterator(arr) {
let index = 0
return {
next: function() {
if(index < arr.length) {
return {done: false, value: arr[index++]}
} else {
return {done: true, value: undefined}
}
}
}
}
const names = ['abc', 'cba', 'nba']
const nums = [10, 20, 30, 40]
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
const numsIterator = createArrayIterator(nums)
console.log(numsIterator.next());
console.log(numsIterator.next());
console.log(numsIterator.next());
接下来,我们再对代码做一个封装。让它变成一个可迭代对象。首先介绍一下什么是可迭代对象。
可迭代对象
可迭代对象是一个对象,它需要符合可迭代协议(iterable protocol)。这个协议需要实现@@iterator 方法。那么在js代码中就要求可迭代对象中具有[Symbol.iterator]这个属性。这个属性是个函数,它要求我们返回一个迭代器。
const iterableObj = {
names: ['abc', 'cba', 'nba'],
[Symbol.iterator]: function () {
let index = 0
return {
// 使用箭头函数,this指向
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
}
在上述代码中,iterableObj就是一个可迭代对象。
const iterator = iterableObj[Symbol.iterator]()
console.log(iterator.next()); // { done: false, value: 'abc' }
console.log(iterator.next()); // { done: false, value: 'cba' }
console.log(iterator.next()); // { done: false, value: 'nba' }
console.log(iterator.next()); // { done: true, value: undefined }
通常我们不会自己生成可迭代对象去遍历数组,因为太麻烦。那么它可以应用到哪里呢?通常我们会使用for...of去遍历,那么这就要求可以遍历的东西必须是一个可迭代对象。
如果我们使用for...of去遍历一个对象,会报TypeError: obj is not iterable的错误,表示obj是不可迭代的。
const obj = {
name: 'haha',
age: 18
}
for(const item of obj) {
console.log(obj);
}
如果我们迭代上述代码的iterableObj呢,它就是一个可迭代对象。
for(const item of iterableObj) {
console.log(item);
}
结果是打印出了数组中的各个元素。
数组本身是个可迭代对象,有[Symbol.iterator]方法,通过调用这个方法,生成可迭代器。
const names = ['abc', 'cba', 'nba']
// console.log(names[Symbol.iterator]);
// 获取到可迭代器
const iterator1 = names[Symbol.iterator]()
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());
所以我们能够使用for...of能遍历数组。除了数组,字符串、map、set、函数中的arguments、nodeList集合都是可迭代对象,都可以使用for...of进行遍历。
// Map/Set
const set = new Set()
set.add(10)
set.add(20)
set.add(30)
console.log(set[Symbol.iterator]);
for(const item of set) {
console.log(item);
}
// 函数中的arguments也是一个可迭代对象
function foo(x, y, z) {
console.log(arguments[Symbol.iterator]);
for(const arg of arguments) {
console.log(arg);
}
}
foo(10, 20, 30)
可迭代对象的应用场景
可迭代对象除了应用在for...of中,还可以应用到很多地方。
(1) JavaScript中语法: for...of、展开运算符、yield*、解构赋值。
(2)创建一些对象时:new Map([iterable])、newWeakMap(iterable)、new Set([iterable])、new WeakSet([iterable])。
(3)一些方法调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable)。
const iterableObj = {
names: ['abc', 'cba', 'nba'],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
}
展开运算符
const names = ['abc', 'cba', 'nba']
const newNames = [...names, ...iterableObj]
console.log(newNames); // [ 'abc', 'cba', 'nba', 'abc', 'cba', 'nba' ]
解构赋值
const [name1, name2] = iterableObj
console.log(name1, name2);
创建一些其他对象时
const set1 = new Set(iterableObj)
const set2 = new Set(names)
console.log(set1);
console.log(set2);
const arr1 = Array.from(iterableObj)
console.log(arr1);
Promise.all
Promise.all(iterableObj).then(res => {
console.log(res);
})
自定义类的迭代
在面向对象开发中,我们通过class来定义一个类,通过类创建出来的对象我们可以添加[Symbol.iterator]方法来使对象默认是可迭代的。
class Classroom {
constructor(address, name, students) {
this.address = address
this.name = name
this.students = students
}
entry(newStudent) {
this.students.push(newStudent)
}
[Symbol.iterator]() {
let index = 0
return {
next: () => {
if(index < this.students.length) {
return {done: false, value: this.students[index++]}
}else {
return {done: true, value: undefined}
}
}
}
}
}
const classroom = new Classroom('6幢606', '六年级六班', ['haha', 'xixi', 'lala'])
classroom.entry('kk')
我们创建出来的对象是可迭代的,可以通过for...of进行遍历。
for(const stu of classroom) {
console.log(stu);
}