[ES6] iteration protocols(迭代协议)
#
iteration protocols(迭代协议)
作为 ECMAScript 2015 (ES6)新增加的一部分,它不是新语法或一个新的内置对象,而是一种协议(protocol)。
这种协议能被任何遵循某些约定的对象实现。
它们是两类协议:可遍历(可迭代)协议 和 迭代器协议。
#一些迭代器是转换自可遍历对象
var someArray = [1, 5, 7];
var someArrayEntries = someArray.entries();
console.log(someArrayEntries.toString()); // "[object Array Iterator]"
console.log(someArrayEntries === someArrayEntries[Symbol.iterator]()); // true
#使用iteration protocols迭代协议的例子
//A String 是内置可遍历对象
var someString = "hi";
console.log(typeof someString[Symbol.iterator]); // "function"
//String's 的默认迭代器返回这个字符串的一个接一个字符
var iterator = someString[Symbol.iterator]();
console.log(iterator + ""); // "[object String Iterator]"
console.log(iterator.next()); // { value: "h", done: false }
console.log(iterator.next()); // { value: "i", done: false }
console.log(iterator.next()); // { value: undefined, done: true }
//一些内置的语法结构, 比如 the spread operator, 在幕后使用同样的iteration protocol
console.log([...someString]); // ["h", "i"]
//可以通过自己的@@iterator方法重新定义迭代行为
someString = new String("hi"); // need to construct a String object explicitly to avoid auto-boxing
someString[Symbol.iterator] = function() {
return { // this is the iterator object, returning a single element, the string "bye"
next: function() {
if (this._first) {
this._first = false;
return {
value: "bye",
done: false
};
} else {
return {
done: true
};
}
},
_first: true
};
};
//重新定义 @@iterator 方法是如何影响内置语法结构的行为的,它使用数据对象相同的迭代协议
console.log([...someString]); // ["bye"]
console.log(someString + ""); // "hi"
可遍历(可迭代)协议
允许 JavaScript 对象去定义或定制它们的迭代行为,
例如(定义)在一个 for..of 结构中什么值可以被循环(得到)。
一些内置类型都是内置的可遍历对象并且有默认的迭代行为,
比如 Array or Map, 另一些类型则不是 (比如Object) 。
为了变成可遍历对象, 一个对象必须实现 @@iterator 方法,
意思是这个对象(或者它原型链prototype chain上的某个对象)必须有一个名字是 Symbol.iterator 的属性:
[Symbol.iterator]
当一个对象需要被遍历的时候(比如开始用于一个for..of循环中),
它的@@iterator方法被调用并且无参数,然后返回一个用于在遍历中获得值的迭代器。
String, Array, TypedArray, Map and Set 是所有内置可遍历对象,
因为它们的原型对象都有一个 @@iterator 方法.
# 自定义可遍历对象
可以实现一个自己的可遍历对象,像这样:
var myIterable = {}; myIterable[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; }; console.log([...myIterable]); // [1, 2, 3]
#接受可遍历对象的内置 APIs
许多 APIs 接受可遍历对象(作为参数),
例如: Map([iterable]), WeakMap([iterable]), Set([iterable]) and WeakSet([iterable]),
还有 Promise.all(iterable), Promise.race(iterable), and Array.from()
var myObj = {}; console.log(new Map([ [1, "a"], [2, "b"], [3, "c"] ]).get(2)); // "b" console.log(new WeakMap([ [{}, "a"], [myObj, "b"], [{}, "c"] ]).get(myObj)); // "b" console.log(new Set([1, 2, 3]).has(3)); // true console.log(new Set("123").has("2")); // true console.log(new WeakSet(function*() { yield {}; yield myObj; yield {}; }()).has(myObj)); // true
#用于可遍历对象的语法
一些语句和表达式是预料会用于可遍历对象,
比如 the for-of loops, spread operator, yield*, and destructuring assignment.
for(let value of ["a", "b", "c"]){ console.log(value); } // "a" // "b" // "c" console.log([..."abc"]); // ["a", "b", "c"] function* gen(){ yield* ["a", "b", "c"]; } console.log(gen().next()); // { value:"a", done:false } [a, b, c] = new Set(["a", "b", "c"]); console.log(a) // "a"
#Non-well-formed 可遍历对象
如果一个可遍历对象的 @@iterator 方法不是返回一个迭代器对象,
那么它就是一个 non-well-formed 可遍历对象 。
使用它可能会发生如下的运行时异常或者buggy行为:
var nonWellFormedIterable = {} nonWellFormedIterable[Symbol.iterator] = () => 1 console.log([...nonWellFormedIterable]) // TypeError: [] is not a function
迭代器协议
该迭代器协议定义了一个标准的方法来产生一个有限或无限序列的值。
当一个对象被认为是一个迭代器时,它实现了一个 next() 的方法
#自制简单迭代器
function makeIterator(array){ var nextIndex = 0; return { next: function(){ return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {done: true}; } }; } var it = makeIterator(['yo', 'ya']); console.log(it.next().value); // 'yo' console.log(it.next().value); // 'ya' console.log(it.next().done); // true
#Infinite iterator
function idMaker(){ var index = 0; return { next: function(){ return {value: index++, done: false}; } }; } var it = idMaker(); console.log(it.next().value); // '0' console.log(it.next().value); // '1' console.log(it.next().value); // '2' // ...
#With a generator
function* makeSimpleGenerator(array){ var nextIndex = 0; while(nextIndex < array.length){ yield array[nextIndex++]; } } var gen = makeSimpleGenerator(['yo', 'ya']); console.log(gen.next().value); // 'yo' console.log(gen.next().value); // 'ya' console.log(gen.next().done); // true function* idMaker(){ var index = 0; while(true) yield index++; } var gen = idMaker(); console.log(gen.next().value); // '0' console.log(gen.next().value); // '1' console.log(gen.next().value); // '2' // ...
#一个生成器对象既是迭代器对象也是可遍历对象:
var aGeneratorObject = function*(){ yield 1; yield 2; yield 3; }(); console.log(typeof aGeneratorObject.next); // "function", because it has a next method, so it's an iterator console.log(typeof aGeneratorObject[Symbol.iterator]); // "function", because it has an @@iterator method, so it's an iterable console.log(aGeneratorObject[Symbol.iterator]() === aGeneratorObject); // true, because its @@iterator method return its self (an iterator), so it's an well-formed iterable console.log([...aGeneratorObject]); // [1, 2, 3]