ES6迭代协议——可迭代协议和迭代器协议

作为 ECMAScript 2015 的一组补充规范,迭代协议并不是新的内置实现或语法,而是协议。这些协议可以被任何遵循某些约定的对象来实现。

可迭代对象(iterable对象)   迭代器对象(Iterator对象)

可迭代协议:(for of循环)

允许对象定义他的可迭代行为,比如在for of结构中,哪些值可以遍历到。在js中的某些类型是内置好的可迭代对象,比如:字符串、数组、类型数组、Map对象、Get对象等。而Object类型不可迭代。这些内置可迭代对象可以进行迭代的原因是内部实现了@@iterator 方法,即在该对象或该对象的原型链上有Symbol.iterator属性实现了@@iterator 方法。

属性:[Symbol.iterator],它的值是:返回一个符合迭代器协议的对象(Iterator对象)的无参数函数。

比如字符串中:原型上有Symbol(Symbol.iterator): ƒ [Symbol.iterator]()

当一个对象需要被迭代的时候(比如被置入一个 for...of 循环时),首先,会调用它的 @@iterator 方法,然后使用此方法返回的迭代器(Iterator对象)获得要迭代的值。

迭代器协议:(Iterator对象)

定义了产生一系列值的标准方式。一个对象必须实现next()方法,才能成为迭代器。

next() 方法必须返回一个对象,该对象应当有两个属性: donevalue

done(boolean)如果迭代器可以产生序列中的下一个值,则为 false

value 迭代器返回的任何 JavaScript 值

小结:

可迭代对象都有@@iterator 方法,该方法挂载在可迭代对象的[Symbol.iterator]属性上,即[Symbol.iterator]属性指向@@iterator 方法的函数体,所以拥有@@iterator 方法的对象称为iterable对象。

该函数体执行返回一个iterator对象,iterator对象上有next方法,next方法执行返回一个对象,包括value值和done值。

我们可以直接执行[Symbol.iterator],从而得到iterator对象,比如:

let arr = [1,2,3];
let arrIterator = arr[Symbol.iterator]();
console.log(arrIterator.next().value); //1

 注意:隐式属性是可以访问的,就像数组的length属性

实例:

以字符串为例

String 是一个内置的可迭代对象:

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

String 的默认迭代器会依次返回该字符串的字符:

let iterator = someString[Symbol.iterator]();
iterator + "";                               // "[object String Iterator]"
 
iterator.next();                             // { value: "h", done: false }
iterator.next();                             // { value: "i", done: false }
iterator.next();                             // { value: undefined, done: true }

一些内置的语法结构——比如展开语法——其内部实现也使用了同样的迭代协议: 

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

我们可以重写@@iterator 方法,那么调用该接口的操作都会受到影响

重写了@@iterator 方法,使用for of遍历:

var someString = new String("hi");
someString[Symbol.iterator] = function() {
  return { // 只返回一次元素,字符串 "bye",的迭代器对象
    index: 0,
    str: "bye",
    next: function() {
      if (this.index < this.str.length) {
        return { value: this.str[this.index++], done: false };
      } else {
        return { done: true };
      }
    }
  };
};

for (let value of someString) {
    console.log(value);         b  y  e
}

我们使用展开运算符:

var someString = new String("hi");
someString[Symbol.iterator] = function() {
  return { // 只返回一次元素,字符串 "bye",的迭代器对象
    index: 0,
    str: "bye",
    next: function() {
      if (this.index < this.str.length) {
        return { value: this.str[this.index++], done: false };
      } else {
        return { done: true };
      }
    }
  };
};
console.log([...someString]);   ["b", "y", "e"]

注意重新定义的 @@iterator 方法是如何影响内置语法结构的行为的,我们进行普通取值时:

console.log(someString+"");  hi

小结:

for...of 和 ...展开运算符,都是通过找@@interator方法,从而找到iterator对象,对iterator对象进行遍历。

探讨自定义 @@iterator 方法

目前所有的内置可迭代对象如下:StringArrayTypedArrayMapSet,它们的原型对象都实现了 @@iterator 方法。而且得到的Iterator对象,都有next方法和@@iterator接口。

普通方法实现:一定要注意,有@@iterator接口的对象,可以进行for of迭代和展开运算符等操作;有next方法的Iterator对象,可以进行next方法的调用,但是该Iterator对象有@@iterator接口,才可以进行for of和展开运算符的操作。

let myIterable = {};
myIterable[Symbol.iterator] = function(){   有迭代接口的对象
  return {
    arr: [1,2,3],
    index: 0,
    next() {
      if(this.index < this.arr.length){
        return {value: this.arr[this.index++], done: false}
      }else {
        return {value: "done is true", done: true}
      }
    }
  }
}
console.log([...myIterable]);  [1, 2, 3]
let myIter = myIterable[Symbol.iterator]();  Iterator对象
myIter.next(); // 1
myIter.next(); // 2
myIter.next(); // 3
console.log(myIter.next().value);  done is true

上述代码,顺便探讨了next方法最后的返回值 

Generator函数实现:

let myIterable = {};
myIterable[Symbol.iterator] = function* (){
  yield 1;
  yield 2;
  yield 3;
}
console.log([...myIterable]);   [1, 2, 3]
let myIter = myIterable[Symbol.iterator]();
console.log(myIter.next().value);  1

接受可迭代对象的内置API

都可以调用Iteraable对象,得到每个api自己想要的东西

...展开运算符,将Iterable对象展开

for...of,迭代遍历Iterable对象

new Set() 和 new Map()接收Iterable对象

Array.from()接收Iterable对象,得到数组

Promise.all() 和 Promise.race()

用于可迭代对象的语法:

for(let value of ["a", "b", "c"]){
    console.log(value);   "a"   "b"   "c"
}

[..."abc"];  ["a", "b", "c"]

function* gen() {
  yield* ["a", "b", "c"];
}

gen().next();  { value: "a", done: false }

[a, b, c] = new Set(["a", "b", "c"]);
a // "a"

生成器对象到底是一个迭代器,还是一个可迭代对象?

生成器对象既是迭代器,也是可迭代对象:

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

let genObj = generator();
console.log(genObj.next().value);  1
for(let value of genObj){
  console.log(value);   2 3 4 5
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值