Iterator 和 for...of 循环

Iterator 和 for...of 循环

参考:http://es6.ruanyifeng.com/#docs/generator

原生具备Iterator接口的数据结构
Array
Map
Set
String
TypedArray
函数的arguments对象
NodeList对象

一个对象如果要具备可被for...of循环调用的Iterator接口,就必须部署方法

class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; }

  next() {
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};
    }
    return {done: true, value: undefined};
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value); // 0, 1, 2
}

默认调用Iterator的场合
1.解构赋值
对数组和Set结构进行解构赋值时,会默认调用

let set = new Set().add('a').add('b').add('c');

let [x,y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

2.扩展运算符
扩展运算符(...)会调用默认的 Iterator 接口

// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

3.yield*
yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口

let generator = function* () {
  yield 1;
  yield* [2,3,4];
  yield 5;
};

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

字符串的Iterator接口
字符串是一个类似数组的对象,也原生具有 Iterator 接口

var someString = "hi";
typeof someString[Symbol.iterator]
// "function"

var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  // { value: undefined, done: true }

可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的

var str = new String("hi");

[...str] // ["h", "i"]

str[Symbol.iterator] = function() {
  return {
    next: function() {
      if (this._first) {
        this._first = false;
        return { value: "bye", done: false };
      } else {
        return { done: true };
      }
    },
    _first: true
  };
};

[...str] // ["bye"]
str // "hi"

遍历器对象的return(),throw()
遍历器对象除了具有next方法,还可以具有return方法和throw方法。
return(),throw()方法可选

如果for...of循环提前退出(出错或有break语句),就会调用return方法
如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法

function readLinesSync(file) {
    return {
        [Symbol.iterator]() {
            return {
                next() {
                    return {done:false};
                },
                return() {
                    file.close();
                    return {done:true};
                }
            }
        }
    }
}


触发return:

//情况1
for(let line of readLinesSync(filename)) {
    console.log(line);
    break;
}
//情况2
for(let line of readLinesSync(filename)) {
    console.log(line);
    throw new Error();
}

for...of循环
内部调用的是数据结构的Symbol.iterator方法
使用范围:数组,Set和Map结构,类似数组对象(arguments对象,DOM NodeList对象),Generator对象,以及字符串for...in循环获取对象的键名
for...of循环获取对象的键值

var arr = ['red', 'green', 'blue'];

for(let a in arr) {
    console.log(a);    //0, 1, 2, 3
}

for(let a of arr) {
    console.log(a);    //a, b, c, d
}

并不是所有类似数组的对象都具有Iterator接口
一个简单的方法就是使用Array.from方法将其转为数组

let arrayLike = { length:2, 0: 'a', 1: 'b'};

for (let x of Array.from(arrayLike)) {
    console.log(x);
}

对象
普通的对象可以用for...in循环遍历键名
不能用for...of

可以使用Object.keys将对象的键名生成一个数组,然后遍历数组

for (var key of Object.keys(someObject)) {
    console.log(key + ':' + someObject[key]);
}

或者使用Generator函数将对象重新包装

function* entries(obj) {
    for(let key of Object.keys(obj)) {
        yield [key, obj[key]];
    }
}
for(let [key, value] of entries(obj)) {
    console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3

与其他遍历语法的比较
for循环:比较麻烦
forEach方法:数组提供的内置方法,无法中途跳出forEach循环,break,return都不行
for...in循环:
    缺点:
        1.数组的键名是数字,但循环出来时字符串
        2.不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键
        3.某些情况下,会以任意顺序遍历键名
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值