乐趣区

『ES6知识点总结』遍历器iterator

『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 接口

具有 iterator 接口的数据结构

【01】原生就具有 Iterator 接口,它们的遍历器接口部署在 Symbol.iterator 属性上:

Array,Object(类数组对象),Map,WeakMap,Set,WeakSet,字符串。

let arr = new Array();

let iterator =arr[Symbol.iterator]();

【02】其他数据结构(主要是对象)的 Iterator 接口,都需要自己部署在 Symbol.iterator 属性上面。(原型链上的对象具有该方法也可)。

【03】一个数据结构只要具有 Symbol.iterator 属性,就可以认为是“可遍历的”(iterable)。
就称为部署了遍历器接口。就可以遍历所有成员。可通过 for of 遍历。

【04】遍历器对象本身也有 Symbol.iterator 方法,执行后返回自身。

gen 是一个 Generator 函数,调用它会生成一个遍历器对象 g。它的 Symbol.iterator 属性,也是一个遍历器对象生成函数,执行后返回它自己。

function* gen(){ // some code}
var g = gen();

g[Symbol.iterator]() === g // true

遍历器过程

【01】zyx456:说白了,等于这些数据结构有一个方法(PS,这个方法称为遍历器函数)。

方法名是 [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()方法。

在 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'
    // ...

使用 while 循环

有了遍历器接口,数据结构就可以用 for…of 循环遍历,也可以使用 while 循环遍历。

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
    var x = $result.value; // ...
    $result = $iterator.next();}

TypeScript 的写法

遍历器接口(Iterable)、指针对象(Iterator)和 next 方法返回值的规格可以描述如下。

interface Iterable {[Symbol.iterator](): Iterator,}

interface Iterator {next(value ? : any): IterationResult, }

interface IterationResult {
    value: any,
        done: boolean,
}

Iterator 接口与 Generator 函数

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 接口的场合

有一些场合会默认调用 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*

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}

其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。

下面是一些例子。

  • for…of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(比如 new Map([[‘a’,1],[‘b’,2]]))
  • Promise.all([])
  • Promise.race([])

字符串的 Iterator 接口

字符串是一个类似数组的对象,原生具有 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)
}
退出移动版