迭代器相关知识点总结

501 阅读1分钟

「这是我参与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);
}

结果是打印出了数组中的各个元素。 image.png 数组本身是个可迭代对象,有[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);
}