当前位置:嗨网首页>书籍在线阅读

02-迭代协议

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

12.1 迭代协议

迭代器本身并不那么有趣,但它却可以支持那些有趣的行为。迭代器协议让任何对象变得可迭代。试想一下,如果你准备创建一个logging类,并且把时间戳作为附加消息。在logging类内部,使用了一个数组来存储时间戳消息:

class Log {
   constructor() {
      this.messages = [];
   }
   add(message) {
      this.messages.push({ message, timestamp: Date.now() });
   }
} 

看起来还不错……但是如果想对log记录进行迭代呢?当然可以这么做,只要访问 log.messages 就行,不过如果log可以像数组一样直接迭代,不是更好吗?迭代器协议就可以实现这个。迭代器协议是说,如果一个类提供了一个符号方法 Symbol.iterator ,这个方法返回一个具有迭代行为的对象(比如:对象有next方法,同时next方法返回一个包含 valuedone 的对象),那么这个类就是可迭代的!修改Log类,给它添加一个 Symbol.iterator 方法吧:

class Log {
   constructor() {
      this.messages = [];
   }
   add(message) {
      this.messages.push({ message, timestamp: Date.now() });
   }
   [Symbol.iterator]() {
      return this.messages.values();
   }
} 

下面可以像数组那样迭代 Log 类的实例了:

const log = new Log();
log.add("first day at sea");
log.add("spotted whale");
log.add("spotted another vessel");
//...
// 像数组一样迭代log        !
for(let entry of log) {
   console.log(`${entry.message} @ ${entry.timestamp}`);
}

在这个例子中,通过从 messages 数组中取出一个迭代器的方式保持迭代器协议,当然也可以编写自己的迭代器:

class Log {
   //...
   [Symbol.iterator]() {
      let i = 0;
      const messages = this.messages;
      return {
         next() {
            if(i >= messages.length)
               return { value: undefined, done: true };
            return { value: messages[i++], done: false };
         }
      } 
   }
} 

至止,以上所使用的例子都是迭代预先定义好元素个数的数组:一本书的页数,或者log中的日期消息记录。其实,迭代器还可以用来表示那些含有无穷值的对象。

为了演示这个,大家来看一个很简单的例子:菲波那切数列。确切地说,菲波那切数列不难生成,但是它们却依赖之前的数字。对于外行人来说,菲波那切数列就是前两个数字的和。这个序列从1和1开始,下一个数字是1+1,也就是2。再下一个数字是1+2,也就是3。第四个数字是2+3,也就是5,以此类推。序列看起来是这样的:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,... 

菲波那切数列永远不会停止。同样,应用程序也不可能知道需要多少元素,这就促使它成为了一个使用迭代器的理想应用。这个例子跟之前的例子唯一不同的地方在于,它的done值永远不会是true:

class FibonacciSequence {
   [Symbol.iterator]() {
      let a = 0, b = 1;
      return {
         next() {
            let rval = { value: b, done: false };
            b += a;
            a = rval.value;
            return rval;
         }
      }; 
   }
}

如果将 for…of 循环用在 FibonacciSequence 的一个实例中,就会得到一个无限循环…菲波那切数列永远都用不完!为了防止这个发生,在循环了10个元素后加一个break语句。

const fib = new FibonacciSequence();
let i = 0;
for(let n of fib) {
   console.log(n);
   if(++i > 9) break;
}