Interator(遍历器)
在 JavaScript 中 迭代器是一个对象,它提供了一个next() 方法,(除了next()方法还有return和throw方法),用来返回序列中的下一项。这个方法返回包含两个属性:done和 value。done属性是个布尔值,代表遍历是否结束,即是否还有必要再一次调用next方法。value属性代表当前成员的值。
迭代器对象一旦被创建,就可以反复调用next()。
function makeIterator(array){ var nextIndex = 0; return { next: function(){ return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {done: true}; } }; } let a = makeIterator(['apple','pear','orange']) console.log(a.next()) // {value: "apple", done: false} console.log(a.next()) // {value: "pear", done: false} console.log(a.next()) // {value: "orange", done: false} console.log(a.next()) // {done: true}
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性, Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。
学习一下新型的for-of循环。可在具有Iterator 接口的元素进行遍历。
let arr = [1,2,3]; for(let v of arr){ v +=1; console.log(v) } // 2 3 4
[1,2,3,4,5].forEach((i,v) => { console.log(v) if(i > 3){ break; } }) // Uncaught SyntaxError: Illegal break statement
for(let v in ['a','b','c']){ console.log(v) } // 0 1 2
let arr = [1,2,3,4]; for(let v of arr){ console.log(v) } // 1 2 3 4
let a = ['a','b','c','d'] for(let v of a){ console.log(v) } // a b c d
for(let v of [1,2,3,4,5]){ console.log(v) if(v > 3){ break } } // 1 2 3 4 5
3 可以遍历其他的所有集合(Nodelist,Set,Map),还有生成器
1234let doms = document.querySelectorAll('div') for(let v of doms){ console.log(v.innerHTML) } // 1 2 3 4
let set = new Set([1,2,3,4,5]) for(let v of set){ console.log(v) } // 1 2 3 4 5
let map = new Map().set('a',1).set('b',2) for(let v of map){ console.log(v) } // ["a", 1] // ["b", 2]
let obj = {name:'peter',age:25} for(let v of obj){ console.log(v) } // Uncaught TypeError: obj is not iterable
let obj = {name:'peter',age:25} for(let v of Object.keys(obj)){ console.log(v) } // name age
4 另外,for-of可以适用于字符串
let str = 'hello'; for(let v of str) { console.log(v) } // h e l l o
Interator不是很难懂,只要明白了哪些是数据集合,就说明具有Interator接口,自然就可以当作遍历器,从而使用for-of循环和使用next方法获取想要的数据。
Generator(生成器)
通过对Interator的理解,生成器也是具有Interator接口的对象,它本身带有遍历特性,返回一个遍历器对象。
既然生成器是遍历器,那么可以使用遍历器的方法(本身函数不会实行,必须通过next()方法才能调用或者使用for-of返回)
function *fn(){ yield 'peter'; yield 1; yield {name:'peter'}; yield [1,2,3,4]; yield function *foo(){ yield 123; } } let a = fn() console.log(a.next()) console.log(a.next()) console.log(a.next()) console.log(a.next()) console.log(a.next()) console.log(a.next()) {value: "peter", done: false} {value: 1, done: false} {value: {…}, done: false} {value: Array(4), done: false} {value: ƒ, done: false} {value: undefined, done: true}
学习一下yield:
function *fn(){ yield 'a'; let b = yield 'b' + 'c'; yield 'd' } let foo = fn() for(let v of foo){ console.log(v) } // a bc d
function *fn(){ yield '1' console.log('start') yield '2' } let a = fn() console.log(a.next()) {value: "1", done: false}
function fn(){ yield 'a' } fn() // Uncaught SyntaxError: Unexpected string
function *fn(){ yield '1'; yield '2'; return '3'; yield '4' } let a = fn() console.log(a.next()); // {value: "1", done: false} console.log(a.next()); // {value: "2", done: false} console.log(a.next()); // {value: "3", done: true} console.log(a.next()); // {value: undefined, done: true} console.log(a.next()); // {value: undefined, done: true}
function *foo(){ yield 'f' } function *fn(){ yield 'a'; yield foo() yield 'b' } for( let v of fn()){ console.log(v) } // a foo {} b
function *foo(){ yield 1; yield 2 } function *fn(){ yield 'a'; yield *foo() yield 'b' } for( let v of fn()){ console.log(v) // a 1 2 b }
上面已经说到生成器也是具有Interator接口的对象,不可置否的,生成器本身带有Symbol.iterator,可以说生成器是遍历器的一种,所以可遍历,可以使用for-of来循环数据。
for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。
function *fn(){ yield 'a'; yield 'b'; yield 'c'; yield 'd'; return 'end' } let a = fn() for(let v of a){ console.log(v) // a b c d }
function *fn(){ yield 'a'; yield 'b'; yield 'c'; yield 'd'; return 'end' } let a = fn() console.log(a.next()); // {value: "a", done: false} console.log(a.next()); // {value: "b", done: false} console.log(a.next()); // {value: "c", done: false} console.log(a.next()); // {value: "d", done: false} console.log(a.next()); // {value: "end", done: true} console.log(a.next()); // {value: undefined, done: true}
function *fn(){ yield 'a'; yield 'b'; yield 'c'; yield 'd'; return 'end' } let a = fn() console.log([...a]) // ["a", "b", "c", "d"]
概要总结;只要具有Symbol.iterator属性的,就可以遍历yield表达式
function* f() { yield 2; yield 3; } new f() // TypeError: F is not a constructor
Generator 函数的方法:
next() 返回 Generator 函数对象中yield后面的表达式,上面已经用到了next方法。yield表达式本身没有返回值,总是返回undefined
function *fn(x){ let a = yield x; let b = yield 2 + a; } let a = fn(2); console.log(a.next(5)); // {value: 2, done: false} console.log(a.next(10)); // {value: 12, done: false} console.log(a.next(20)); // {value: undefined, done: true}
function *fn(x){ let a = yield x; let b = yield 2 + a; } let a = fn(2); console.log(a.next()); // {value: 2, done: false} console.log(a.next()); // {value: NaN, done: false} console.log(a.next()); // {value: undefined, done: true}
throw() 在函数体外抛出错误,然后在 Generator 函数体内捕获。
let a = function* () { try { yield ; } catch ( e ){ console.log(e); } }; var i = a(); console.log(i.throw()) // Uncaught undefined
let g = function* () { try { yield 1; } catch (e) { console.log(e); } yield 'a'; yield 'b' }; let i = g(); console.log(i.next()) // 1 i.throw(new Error('出错了!')); // Error: 出错了!(…) 附带执行了一次yield ‘a’ console.log(i.next()) // b
throw()方法的作用就是捕获异常,并且继续执行下去,不因为异常而中断。throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法。
throw()的意义:大大方便了对错误的处理。多个yield表达式,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在 Generator 函数内部写一次catch语句就可以了。
return() 返回给定的值,并且终结遍历 Generator 函数。
function *fn(){ yield 1; yield 2; return 3; } let a = fn(); console.log(a.next()) // {value: 1, done: false} console.log(a.return()); // {value: undefined, done: true} console.log(a.next()) // {value: undefined, done: true}
function *fn(){ yield 1; yield 2; return 3; } let a = fn(); console.log(a.next()) // {value: 1, done: false} console.log(a.return(100)); // {value: 100, done: true} console.log(a.next()) // {value: undefined, done: true}
return()的意义:通常在生成器异步操作时需要在某个时段跳出来。
Generator生成器是异步编程提供了方便。
对于Interator和Generator,在平时使用时很少用到,只有那个for-of可以替代for循环使用,主要用于异步编程async当中。学习这个感觉没学全,过后我会再仔细学一遍。知识点我放到github里了,有需要可以去下载一起学习。
还是那句话。有什么问题或错误请私信或者下方评论,一起讨论进步。
参考资料:
阮一峰es6入门 http://es6.ruanyifeng.com/