当前位置:K88软件开发文章中心编程语言JavaScriptECMAScript → 文章内容

ECMAScript 6 Iterator和for...of循环

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 15:38:36

才会被for...of循环遍历。对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作Map结构使用,ES5没有Map结构,而ES6原生提供了。一个对象如果要有可被for...of循环调用的Iterator接口,就必须在Symbol.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}; } else { return {done: true, value: undefined}; } }}function range(start, stop) { return new RangeIterator(start, stop);}for (var value of range(0, 3)) { console.log(value);}上面代码是一个类部署Iterator接口的写法。Symbol.iterator属性对应一个函数,执行后返回当前对象的遍历器对象。下面是通过遍历器实现指针结构的例子。function Obj(value) { this.value = value; this.next = null;}Obj.prototype[Symbol.iterator] = function() { var iterator = { next: next }; var current = this; function next() { if (current) { var value = current.value; current = current.next; return { done: false, value: value }; } else { return { done: true }; } } return iterator;}var one = new Obj(1);var two = new Obj(2);var three = new Obj(3);one.next = two;two.next = three;for (var i of one){ console.log(i);}// 1// 2// 3上面代码首先在构造函数的原型链上部署Symbol.iterator方法,调用该方法会返回遍历器对象iterator,调用该对象的next方法,在返回一个值的同时,自动将内部指针移到下一个实例。下面是另一个为对象添加Iterator接口的例子。let obj = { data: [ 'hello', 'world' ], [Symbol.iterator]() { const self = this; let index = 0; return { next() { if (index < self.data.length) { return { value: self.data[index++], done: false }; } else { return { value: undefined, done: true }; } } }; }};对于类似数组的对象(存在数值键名和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'}注意,普通对象部署数组的Symbol.iterator方法,并无效果。let iterable = { a: 'a', b: 'b', c: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator]};for (let item of iterable) { console.log(item); // undefined, undefined, undefined}如果Symbol.iterator方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。var obj = {};obj[Symbol.iterator] = () => 1;[...obj] // TypeError: [] is not a function上面代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数,因此报错。有了遍历器接口,数据结构就可以用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方法),都检查一下返回值的done属性,如果遍历还没结束,就移动遍历器对象的指针到下一步(next方法),不断循环。调用Iterator接口的场合有一些场合会默认调用Iterator接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。(1)解构赋值对数组和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'];(2)扩展运算符扩展运算符(...)也会调用默认的iterator接口。// 例一var str = 'hello';[...str] // ['h','e','l','l','o']// 例二let arr = ['b', 'c'];['a', ...arr, 'd']// ['a', 'b', 'c', 'd']上面代码的扩展运算符内部就调用Iterator接口。实际上,这提供了一种简便机制,可以将任何部署了Iterator接口的数据结构,转为数组。也就是说,只要某个数据结构部署了Iterator接口,就可以对它使用扩展运算符,将其转为数组。let arr = [...iterable];(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 }(4)其他场合由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。for...ofArray.from()Map(), Set(), WeakMap(), Weak

上一页  [1] [2] [3] [4]  下一页


ECMAScript 6 Iterator和for...of循环