js迭代器iterator

JavaScript050

js迭代器iterator,第1张

iterator(迭代)一般很少直接使用, 但是却是很常用很重要的功能.

例如 :

对象的扩展运算符(...)内部其实是调用了 Iterator 接口。

在es6 中统一了遍历的接口 Iterator,Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。

字符串也可以使用扩展运算符

rest运算符, 与扩展运算符是逆运算

扩展运算符:数组=>分割序列

rest运算符:分割序列=>数组

rest可以代替arguments变量

回到主题, 迭代

Symbol.iterator 为每一个对象定义了默认的迭代器。

当需要对一个对象进行迭代时(比如开始用于一个 for..of 循环中),它的 @@iterator 方法都会在不传参情况下被调用,返回的 迭代器 用于获取要迭代的值。

一些内置类型拥有默认的迭代器行为,其他类型(如 Object )则没有。下表中的内置类型拥有默认的 @@iterator 方法:

更多信息请参见 迭代协议 。

js中对象分为可迭代和不可迭代 如果是可迭代哪它就会有一个[Symbol.iterator] 函数

这个函数就是对象的迭代器函数,如用for of 如果遍历的对象没有这个迭代方法那么就会报错.

for of 传入的是可迭代对象,但是如何吧一个不可迭代的对象变为可迭代的对象呢!很简单就是自己写一个[Symbol.iterator]函数。

你以为迭代就这么简单的结束了吗?现在再讲一个新东西 yield

. yield是ES6的新关键字,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。

. yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。

. yield无法单独工作,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性。

yield 自定义一个迭代器

function* () {} 这种函数名字叫 generator函数, 生成器函数, 下一篇再重点讲这个哈.

迭代:

1、自己调用自己的方法,或者称递归。这个应该不用多说。

2、还有一种解释:很多程序有迭代器。这是一种用于“遍历”数组的工具对象。一般遍历一个数组都是使用循环变量从0到最后一个,或者使用链表遍历量表中内容。使用迭代器可以不关注数组的具体实现方式,遍历数组中所有成员。

遍历:

对数据结构中每一个成员都进行一次访问的操作就是遍历。比如遍历列表。最为常见得问题是遍历树,遍历树的常见方法有:先序/中序/后序,或者按照另一个纬度划分有,广度遍历和深度遍历。相关具体算法可以查找其他资料。

这个问题分类有误,应该标记为计算机、程序。

引用一段大神的介绍:

迭代器EDIT

迭代器是一个对象,知道如何存取物品从一个集合一次取出一项, 而跟踪它的当前序列所在的位置。在  JavaScript 中 迭代器是一个对象,它提供了一个 next() 方法返回序列中的下一个项目。当这个序列消亡时这个方法可以随时抛出一个StopIteration exception(异常)。

一旦创建,迭代器对象可以显式地通过不断调用 next(),或隐式地使用 JavaScript 的 for...in  和 for each 构造。

简单的迭代器对象和数组可以使用 Iterator() 函数创建(下面是一个简单的对像):

var lang = { name: 'JavaScript', birthYear: 1995 }

var it = Iterator(lang)

一旦初始化,next()方法可以用来依次访问对象中的键-值:

var pair = it.next()// Pair is ["name", "JavaScript"]

pair = it.next()// Pair is ["birthYear", 1995]

pair = it.next()// A StopIteration exception is thrown

一个 for...in 循环结构可以直接取代next()方法的调用。 当StopIteration exception异常抛出时这个循环会自动终止。

var it = Iterator(lang)

for (var pair in it)

 print(pair)// prints each [key, value] pair in turn

假如我们只想遍历一个对象的 keys 时,我们可以通过把第二个参数设置为 true 来实现:

var it = Iterator(lang, true)

for (var key in it)

 print(key)// prints each key in turn

使用 Iterator() 来访问对象内容的优点是对于那些已经加入对象的自定义属性(不用管属性名,一股脑的访问,或者只访问属性,上面就是这样)。它的原型不会包含在序列中。

Iterator() 也可以用于数组:

var langs = ['JavaScript', 'Python', 'C++']

var it = Iterator(langs)

for (var pair in it)

 print(pair)// prints each [index, language] pair in turn

基于对象,通过传递true作为第二个参数,将会导致迭代结果发生数组索引上益(会吗?我也没搞明白,你知道的话请告诉我):

var langs = ['JavaScript', 'Python', 'C++']

var it = Iterator(langs, true)

for (var i in it)

 print(i)// prints 0, then 1, then 2

也可以指定块作用域变量(let是个javascript1.7的关键字,可以使用 let 建立只存在于 for 循环上下文中的变量)包括索引和值(index and value)在for…in循环中使用let关键字和非结构化赋值:

var langs = ['JavaScript', 'Python', 'C++']

var it = Iterator(langs)

for (let [i, lang] in it)

print(i + ': ' + lang)// prints "0: JavaScript" etc.

定义自定义迭代器EDIT

某些对象遍历集合的项目时应该使用特定的方式遍历。

遍历一个序列对象时应该是一个接着一个。

遍历一棵树时应该使用深度优先或者广度优先方式。

遍历一个数据库查询结果对象时应该是一条一条地遍历,即使整个结果并没有被加载到一个数组中。

一个迭代器在一个无限的数学序列(如斐波那契序列)应该能够一个一个地返回结果而不创建一个无限长度的数据结构。

JavaScript可以让你编写代码来表示自定义迭代逻辑并链接到一个对象。

我们来创建一个简单的序列对象用来存放low和high。

function Range(low, high){

 this.low = low

 this.high = high

}

现在我们要创建一个遍历器,可以返回这个序列中的一序列包容性的整数,这个遍历器接口要求我们我们实现一个next()方法,这个next()方法返回这个序列或者抛出一个StopIteration exception异常。

function RangeIterator(range){

 this.range = range

 this.current = this.range.low

}

RangeIterator.prototype.next = function(){

 if (this.current >this.range.high)

   throw StopIteration

 else

   return this.current++

}

我们的RangeIterator被一个序列实例所序列化,并且维持它自己当前的属性属性所在位置。

最后,为了RangeIterator与Range序列对象,我们需要给Range写一个特殊的__iterator__方法,当我们试着遍历这个Range序列时会调用这个方法,并且应该返回一个RangeIterator遍历器实例。

Range.prototype.__iterator__ = function(){

 return new RangeIterator(this)

}

下面就写段代码来实验下我们自定义的遍历器吧:

var range = new Range(3, 5)

for (var i in range)

 print(i)// prints 3, then 4, then 5 in sequence

生成器(Generators): 一个更好的方法来构建遍历器EDIT

虽然迭代器是一个有用的工具,但是由于需要显示地维持他们的内部状态,所以他们的构造需要仔细的规划(看得我眼花呀)。生成器给你提供了一个强大的选择:它允许你通过写一个可以保存自己状态的的简单函数来定义一个迭代算法。

一个生成器其实是一种特殊类型的函数(这个函数作为一个为迭代器工作的工厂),一个函数如果它里面包含了一个或一个以上的yield表达式,那么这个函数就成为一个生成器了。

Note: 这个yield关键字只能被包含在下面的代码快中才有用 <script type="application/javascriptversion=1.7">  (或者更高版本).XUL脚本标记访问这些功能不需要这个特殊的块。

当一个生成器函数被一个函数体调用时并不会马上执行;相反,它会返回一个generator-iterator对象。每次调用generator-iterator的next()方法将会执行这个函数体至下一个yield表达式并且返回一个结果。当函数执行完或者执行到一个return时抛出一个StopIteration exception异常。

用一个案例来阐述:

function simpleGenerator(){

 yield "first"

 yield "second"

 yield "third"

 for (var i = 0i <3i++)

   yield i

}

var g = simpleGenerator()

print(g.next())// prints "first"

print(g.next())// prints "second"

print(g.next())// prints "third"

print(g.next())// prints 0

print(g.next())// prints 1

print(g.next())// prints 2

print(g.next())// StopIteration is thrown

一个生成器函数可以直接用作为一个类的__iterator__方法,大大的减少了创建一个自定义迭代器的代码量,下面是Range重写成一个生成器:

function Range(low, high){

 this.low = low

 this.high = high

}

Range.prototype.__iterator__ = function(){

 for (var i = this.lowi <= this.highi++)

   yield i

}

var range = new Range(3, 5)

for (var i in range)

 print(i)// prints 3, then 4, then 5 in sequence

并不是所有的生成器都会终止;它也可以创建一个无穷序列的生成器。下面实现了斐波纳契数列的生成器:

function fibonacci(){

 var fn1 = 1

 var fn2 = 1

 while (1){

   var current = fn2

   fn2 = fn1

   fn1 = fn1 + current

   yield current

 }

}

var sequence = fibonacci()

print(sequence.next())// 1

print(sequence.next())// 1

print(sequence.next())// 2

print(sequence.next())// 3

print(sequence.next())// 5

print(sequence.next())// 8

print(sequence.next())// 13

生成器可以有参数,但被限制在函数第一次调用时。生成器可以通过一个return语句来终止(因为他们将抛出一个topIteration exception)。下面是一个变异的fibonacci()函数有一个limit参数,一旦满足条件将终止。

function fibonacci(limit){

 var fn1 = 1

 var fn2 = 1

 while (1){

   var current = fn2

   fn2 = fn1

   fn1 = fn1 + current

   if (limit &&current >limit)

     return

   yield current

 }

}