最近看阮一峰阮大神的ES6,刚刚看到Iterator和for...of循环这一章,小做笔记跟你们略微分享一下,不足之处还望你们多多指正es6
Iterator(遍历器)就是一种机制;任何数据结构只要是部署了iterator接口,就能够完成遍历操做(即依次处理该数据的全部成员);数组
Iterator的做用有三个:一是为各类数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员可以按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。(阮大神原话);数据结构
只要是一个对象部署了Symbol.interator接口,就能够用for...of遍历该对象,同时也能够调用该接口的Symbol.interator方法调用next()方法对对象进行遍历,不一样的是for..of是对该对象的值的输出,而next()返回的是对象。
例以下面的例子:函数
若是调用next方法就是this
调用next()方法会返回一个对象{value:value;done:true or false};直到对遍历对象的值便利完成以后,next()方法会返回一个{value:undefined; done:true}表明已经遍历完成,再调用next()方法也不会报错可是依旧会返回{value:undefined; done:true};spa
有一些对象是原生就封装好的Symbol.iterator接口,能够直接调用,固然也能够直接用for...of遍历;
在ES6中,有三类数据结构原生具有Iterator接口:数组、某些相似数组的对象、Set和Map结构。
若是没有原生的Symbol.iterator接口,想用for...of遍历,就须要本身在该对象中部署Symbol.iterator接口,例如prototype
一个对象若是要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具备该方法也可)。code
一个对象若是要有可被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属性对应一个函数,执行后返回当前对象的遍历器对象。
原型链上部署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; var done = current == null; current = current.next; return { done: done, 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
对象内部部署接口
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 }; } } }; } };
也就是说若是一个对象没有Symbol.iterator接口,能够在类自己上面部署,也能够在原型连上部署,也能够在对象内部部署
对于相似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的Iterator接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; // 或者 NodeList.prototype[Symbol.iterator] = [][Symbol.iterator]; [...document.querySelectorAll('div')] // 能够执行了
具体请看例子
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' }
**可是请记住,这个方法仅仅适用于类数组对象,上面的也能够直接用Array.from(iterable)转换成数组来遍历,例如
let arrayLike = { length: 2, 0: 'a', 1: 'b' }; for (let x of Array.from(arrayLike)) { console.log(x); }//a b
对于普通对象这两个方法是无论用的,**例如
let iterable = { edition: 'a', writer: 'b', read: 'c', length: 3, }; for (let item of Array.from(iterable)) { console.log(item); }
上面的代码就会输出三个undefined
let iterable = { edition: 'a', writer: 'b', read: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; for (let item of iterable) { console.log(item); //报错 }
上面的代码就会抛异常的;
其实ES6不少地方都用的到Iterator这个接口,例如:
解构赋值
扩展运算符
yield*
最后须要说下,字符串既是类数组对象同时本身也有原生Symbol.iterator接口能够直接调用,例如
var str = "hell";
for(let v of str){
console.log(v);//"h","e","l","l"
}
对于字符串来讲,for...of循环还有一个特色,就是会正确识别32位UTF-16字符。
for...of 区别于for循环,for循环比较麻烦,可是是最原始的方法;
for...of 区别于数组的forEach方法,由于forEach方法是从头至尾执行,不会跳出,可是遇到break或者return,continue会跳出循环
有着同for...in同样的简洁语法,可是没有for...in那些缺点。
不一样用于forEach方法,它能够与break、continue和return配合使用。
提供了遍历全部数据结构的统一操做接口。
更详细的请查看阮大神(http://es6.ruanyifeng.com/#docs/iterator)本章内容