『ES6知识点总结』遍历器iterator
本文内容以下:数组
1 具备iterator接口的数据结构 2 遍历器过程 3 遍历器做用: 4 模拟next()方法 5 使用while循环 6 TypeScript的写法 7 Iterator接口与Generator函数 8 对象的遍历器接口 8.1 对于相似数组的对象 9 调用Iterator接口的场合 9.1 解构赋值 9.2 扩展运算符 9.3 yield* 9.4 其余场合 10 字符串的Iterator接口
【01】原生就具备Iterator接口,它们的遍历器接口部署在Symbol.iterator属性上:数据结构
Array,Object(类数组对象),Map,WeakMap,Set,WeakSet,字符串。函数
let arr = new Array();prototype
let iterator =arr[Symbol.iterator]();指针
【02】其余数据结构(主要是对象)的Iterator接口,都须要本身部署在Symbol.iterator属性上面。(原型链上的对象具备该方法也可)。rest
【03】一个数据结构只要具备Symbol.iterator属性,就能够认为是“可遍历的”(iterable)。
就称为部署了遍历器接口。就能够遍历全部成员。可经过for of 遍历。code
【04】遍历器对象自己也有Symbol.iterator方法,执行后返回自身。对象
gen是一个Generator函数,调用它会生成一个遍历器对象g。它的Symbol.iterator属性,也是一个遍历器对象生成函数,执行后返回它本身。接口
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === g // true
【01】zyx456:说白了,等于这些数据结构有一个方法(PS,这个方法称为遍历器函数)。ip
方法名是[Symbol.iterator],方法中返回一个对象(PS,这个对象称为遍历器对象),该对象有一个next()方法。这个next()会遍历数据结构的成员。返回{value:XX||undefined , done:true||false}
let obj = {
[Symbo.iterator] (){
return {next(){}}
}
}
调用遍历器函数,就会返回一个遍历器对象。
每次调用iterator.next(),就使用该数据结构的一个成员,成员的值就是返回对象中value的值。done为false,最后会返回一个有value和done两个属性的对象。
从头至尾依次使用数据结构的成员。直到使用的成员都访问完了。
当数据结构的成员已所有访问了,此时,再调用iterator.next()。返回的对象中,value的值为undefined,done为true。
done属性是一个布尔值,表示遍历是否结束。
let arr = [1,2,3];
let iterator = arr[Symbol.iterator]();
iterator.next();//{value:"",done:false};
let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator]();//返回遍历器对象。 iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true }
【03】遍历器与它所遍历的那个数据结构是分开的。
一是为各类数据结构提供一个统一的访问接口;
二是让数据结构的成员能够按某种次序排列;
三是供for...of使用。
使用一个函数,函数参数是数组,返回一个对象,对象具备next()方法。
在next()中,使用一个变量,遍历数组的下标。经过和数组的长度对比,来判断返回不一样的对象。
返回对象都有value和done属性。
function Convert(arr){ let index = 0; return { next (){ if(index<arr.length){ return {value:arr[index++],done:false} } else { return {value:undefined,done:true} } } } } var it = Convert(['a', 'b']); it.next() // { value: "a", done: false } it.next() // { value: "b", done: false } it.next() // { value: undefined, done: true }
zyx456:next返回的就是指针对象了。此时,it是对象,它的变量index是保存在函数中的。
对于遍历器对象来讲,done: false和value: undefined属性都是能够省略的,所以上面的Covert函数能够简写成下面的形式。
function Covert(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? { value: array[nextIndex++] } : { done: true }; } } }
【】下面是一个无限运行的遍历器对象的例子。
zyx456:仅了解。实用性不大。
遍历器生成函数idMaker,返回一个遍历器对象(即指针对象)。可是并无对应的数据结构,或者说,遍历器对象本身描述了一个数据结构出来。
function idMaker() { let index = 0; return { next: function() { return { value: index++, done: false }; } } }var it = idMaker(); it.next().value // '0' it.next().value // '1' it.next().value // '2' // ...
有了遍历器接口,数据结构就能够用for...of循环遍历,也可使用while循环遍历。
var $iterator = ITERABLE[Symbol.iterator](); var $result = $iterator.next(); while (!$result.done) { var x = $result.value; // ... $result = $iterator.next(); }
遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格能够描述以下。
interface Iterable { [Symbol.iterator](): Iterator, } interface Iterator { next(value ? : any): IterationResult, } interface IterationResult { value: any, done: boolean, }
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3] // 或者采用下面的简洁写法 let obj = { * [Symbol.iterator]() { yield 'hello'; yield 'world'; } }; for (let x of obj) { console.log(x); } // hello // world
【】对象部署遍历器接口并非很必要,可使用Map。
(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的Iterator接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; // 或者 NodeList.prototype[Symbol.iterator] = [][Symbol.iterator]; [...document.querySelectorAll('div')] // 能够执行了
下面是相似数组的对象调用数组的Symbol.iterator方法的例子。
let iterable = { 0: 'a', 1: 'b', 2: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; for (let item of iterable) { console.log(item); // 'a', 'b', 'c' }
有一些场合会默认调用Iterator接口(即Symbol.iterator方法)。
对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator方法。
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'];
扩展运算符(...)会内部调用默认的iterator接口。
能够将任何部署了Iterator接口的数据结构,转为数组。
// 例一 var str = 'hello'; [...str] // ['h','e','l','l','o'] // 例二 let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
let arr = [...iterable];
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接口。
var someString = "hi"; typeof someString[Symbol.iterator] // "function" var iterator = someString[Symbol.iterator](); iterator.toString(); // => '[object String Iterator]' iterator.next() // { value: "h", done: false } iterator.next() // { value: "i", done: false } iterator.next() // { value: undefined, done: true }
let some = "book"; for (let i of some){ console.log(i) }