首页 > 编程语言 >JavaScript迭代器与生成器

JavaScript迭代器与生成器

时间:2023-02-10 19:34:47浏览次数:49  
标签:console log 迭代 对象 JavaScript 生成器 let

JavaScript的迭代器与生成器

前沿:

  可迭代对象及其相关的迭代器是是ES6的一个特性。数组是可迭代的,字符串、set对象和map对象也是。这意味着这些数据结构的内容可以通过for/of循环来迭代。

例:

let sum =0;
for(let i of [1,2,3]{
sum+=i;
}
sum//6

迭代器让...操作符能够展开或“扩展”可迭代对象,

let chars = [..."abcd"];

let data  = [1,2,3,4,5];

Math.max(...data)//5

迭代器也可以用于解构赋值:

let  d = Uint8Array.of(255,0,1,199);
let [r,g,b,a] = d;
console.log(a)//199

迭代Map对象时,返回值是【key,vaule】对,在for/of循环中可以直接使用解构赋值:

let m  =new Map([["one",1],["weo",2]]);
for (let[k,V] of m)
console.log(k,V)//one 1 weo 2

如果只想迭代键或值,而不是键/值对,可以使用keys()或者values()的方法;

let m  =new Map([["one",1],["weo",2]]);
console.log([...m]);//默认迭代//(2) ['one', 1] ['weo', 2]
console.log([...m.entries()])//enteries方法相同(2) ['one', 1] ['weo', 2]
console.log([...m.keys()])//方法只迭代键(2) ['one', 'weo']
console.log([...m.values()])//只迭代值(2) [1, 2]

最后,有些会接受Array对象的内置函数和构造函数(在ES6及之后的版本中)可以接收任意迭代器。,例如,set()构造函数就是这样一个API:

//字符串是可以迭代的,因此两个集合相同。
console.log( new Set("abc"))//Set(3) {'a', 'b', 'c'}

本章解释迭代器的原理,并展示如何创建可迭代的数据结构。理解了迭代器的基本概念后,我们在讲解再生器。生成器也是ES6的一个新的特性,主要用于简化迭代器的创建。

迭代器原理

摘要:

for/of循环和扩展操作符可以直接操作可迭代对象,但那有必要了解这种迭代是如何发生的。要理解JavaScript中的这种迭代,必须理解3个不同的类型。首先是可迭代对象,类似于Array、set、map都是可以迭代的。其次是迭代器对象,用于执行迭代。最后是迭代结果对象,保存每次迭代的结果。

可迭代对象指的是任何具有专用的迭代器方法,且该方法返回且代其对象的对象。迭代器对象指的是任何具有next()方法,且该方法返回迭代结果对象的对象。迭代结果对象是具有属性value和done的对象。要迭代一个可迭代对象,首先要调用其迭代器方法获得一个迭代器对象。然后,重复调用这个迭代器对象的next()方法,直至返回done属性为true的迭代结果对象。这里比较特别的是,可迭代器的迭代方法没有惯用名称,而是使用了符号Symbol.iterator作为名字。因此可迭代对象iterable的简单for/of循环也可以写成如下这种形式:

let iterable = [99];
let iterator = iterable[Symbol.iterator]();
for(let result = iterator.next();!result.done;result = iterator.next()){
    console.log(result.value)//99
}

内置可迭代数据类型的迭代器对象本身也是可以迭代的(也就是说,他们有一个名为symbol.iterator的方法,返回他们自己)。在下面的代码所示的需要迭代“部分使用”的迭代器时,这种设计是有用的:

let list = [1,2,3,4,5]
let iter = list[Symbol.iterator]();
console.log(iter)
let head = iter.next();
console.log(head)//1
let tail = [...iter]
console.log(tail)//[2,3,4,5]

实现可迭代对象

在ES6中。可迭代对象非常重要。因此,只要你的数据类型表示某种可迭代的结构,就应该考虑把他们实现为可迭代对象。Range类就是可迭代的。那些类使用生成器函数把自己转换成可迭代的类。

为了让类可迭代,必须实现一个名为Symbol.iterator的方法。这个方法必须返回一个迭代器对象,该对象有一个next()方法。而这个next()方法必须返回一个迭代结果对象,该对象有一个value属性和/或一个布尔值done属性。下例实现了一个可迭代的Range类,演示了如何创建可迭代对象、迭代器对象和迭代结果对象。

// Range对象表示一个数值范围{x:from<=x<=to}
// Range定义了has()方法用于测试给定数值是不是该范围的成员
// Range是可迭代的,迭代器范围内所有的整数

class Range{
    constructor (from,to){
        this.from = from;
        this.to = to;
    }
    //让range对象数值集合一样
    has(x){return typeof x ==="number"&&this.from<=x&&x<=this.to;}
    //使用几何表示法返回当前范围的字符串表示
    toString(){return`{x|${this.from}}<=x<=${this.to}`}
    //通过返回一个迭代器对象,让Range对象可迭代
    // 注意这个方法的名字是一个特殊符号,不是字符串
    [Symbol.iterator](){
        //每个迭代器实例必须相互独立、互不影响地迭代自己的范围
        //因此需要一个状态变量跟踪迭代的位置。从第一个大于等于form整数开始
        let next = Math.ceil(this.from);//这是下一个要返回的值
        let last = this.to; //不会返回大于它的值
        return{
            //这个next()方法是迭代器的标志,他必须返回一迭代器结果对象
            next(){
                return(next <= last)//如果还没有返回last
                ?{value:next++}//则返回next并给它加1
                :{done:true}//否则返回表示完成的对象

            }
            //为了方便起见,迭代器本身也可迭代
        }
        
    }
}
for (let x of new Range(1,10))
console.log(x);//打印数值1到10
console.log(...new Range(-2,2))//-2 -1 0 1 2

 

可迭代对象与迭代器有一个重要的特点,他们天性懒惰:如果计算下一个值需要一定的计算量,则相应的计算会推迟到实际需要下一个值的时候在发生。例如,假设假设有一个非常长的文本字符串,你想对他们进行分词,返回以空格分隔的单词。如果使用字符串的split()方法,哪怕是一个单词都还没用也要处理整个字符串,这样可能会占用很多的内存来保存反回的数组和其中的字符串。下面这个函数可以对字符串中的单词进行懒惰迭代,不必把它们全部保存在内存里:

function words(s){
    var r = /\s+|$/g;//匹配一个或多个空格或末尾
    r.lastIndex = s.match(/[^]/).index;//开始匹配第一个非空格
    return{//返回一个可迭代的迭代器容器
        [Symbol.iterator](){//这个方法是可迭代对象必须的
            return this;
        },
        next(){//这个方法是迭代器必须的
            let start = r.lastIndex;//从上次匹配的地方恢复
            if(start<s.length){//如果还没有处理完
             let match = r.exec(s);
             if(match){
                return {value:s.substring(start,match.index)};
             }   
            }
             return {done:true}
        }
       
    }
}
console.log([...words("abc def ghi!")])

定义生成器

在使用关键字 function 定义函数对象时,通过在关键字 function 后面添加星号 * 来定义生成器。

不能使用箭头函数定义生成器。

星号 * 不受其两侧的空格影响。

function *generator() {}
function * generator01() {}
function* generator02() {}

let generator03 = function *() {}

// 在对象字面量中简写函数时
let obj = { 
    *generator04() {}
}

创建生成器对象

调用生成器并不会执行生成器中的代码,而是会创建一个生成器对象。

function *generator() {
    return 'finished'
}

// 创建生成器对象
let generatorObj = generator()

生成器对象是迭代器。生成器对象实现了接口 Iterator ,拥有方法 next() 。但生成器对象的方法 next() 并不是用于迭代可迭代对象的。

生成器对象是可迭代对象。生成器对象也实现了接口 Iterable ,而且生成器对象的默认迭代器函数返回生成器对象自身。

中断生成器

在生成器中使用关键字 yield 来指定生成器中断执行的位置。

生成器执行的中断、恢复:

  • 生成器在执行时遇到关键字 yield ,会停止执行,函数作用域的状态会被保留。
  • 当生成器的生成器对象调用方法 next() 时,生成器会从停止执行的位置开始恢复执行,直到遇到关键字 yield 或 return ,生成器才会停止执行。

关键字 yield 像关键字 return 一样可以返回值。关键字 yield 只能在生成器的函数块中使用,不能在普通函数的函数块中使用。

例如:

function *generator() {
    yield '01'  // 中断执行,并返回值
    yield '02'
    return 'finished'
}

let generatorObj = generator()

console.log(generatorObj.next())
console.log(generatorObj.next())
console.log(generatorObj.next())

// 输出:
// { done: false, value: '01' }
// { done: false, value: '02' }
// { done: true, value: 'finished' }

提前终止生成器

通过调用生成器对象的方法 return() ,提前终止生成器。

生成器对象的方法 return() 会强制生成器进入关闭状态。

生成器对象的方法 return() 

例:

function *generator() {
    yield '01'
    yield '02'
    return 'finished'
}

let generatorObj = generator()

console.log(generatorObj.next())
console.log(generatorObj.return('Exiting early'))  // 提前终止生成器
console.log(generatorObj.next())

// 输出:
// { done: false, value: '01' }
// { done: true, value: 'Exiting early' }
// { done: true, value: undefined }

生成器迭代可迭代对象

生成器通过在关键字 yield 和可迭代对象之间添加星号 * ,来迭代一个可迭代对象。

星号 * 不受其两侧的空格影响。

生成器迭代可迭代对象的主要过程:

  • 创建可迭代对象的迭代器。
  • 调用迭代器的方法 next() 。
  • 判断迭代器结果的属性 done 。
  • 如果迭代器结果的属性 done为 false ,则使用关键字 yield 返回迭代器结果的属性 value 的值。并转到过程 2 。
  • 如果迭代器结果的属性 done为 true,则获取迭代器结果的属性 value 的值。
  • 结束。

因此生成器会将可迭代对象的迭代器最终返回的迭代器结果 { done: true, value: any } 的属性 value 的值作为 yield* 最终的值,但不会停止执行来返回 yield* 的值。

例:

function *generator() {
    yield* [1, 2]  // 迭代可迭代对象
    return 'finished'
}

let generatorObj = generator()

console.log(generatorObj.next().value)
console.log(generatorObj.next().value)
console.log(generatorObj.next().value)

// 输出:
// 1
// 2
// finished

function *generator01() {
    console.log('yield*: ', yield* [1, 2])
    return 'finished'
}

generatorObj = generator01()

console.log(generatorObj.next().value)
console.log(generatorObj.next().value)
console.log(generatorObj.next().value)

// 输出:
// 1
// 2
// yield*: undefined
// finished

 

标签:console,log,迭代,对象,JavaScript,生成器,let
From: https://www.cnblogs.com/user-zbb/p/17099538.html

相关文章

  • 浏览器中的JavaScript(3)
    3.操作CSS摘要:我们已经知道了JavaScript可以控制HTML文档的逻辑结构和内容。通过对CSS编程,Javascript也可以控制文档的外观和布局。接下来讲解几种JavaScript可以用来操作......
  • Python3,我只用一段代码,就写了个词云生成器,YYDS!
    1、引言小鱼:小屌丝,你在干啥呢?小屌丝:鱼哥,你看,我的PPT写的高大尚不。小鱼:这有啥高大尚的啊,小屌丝:你仔细看,往下翻一页小鱼:额。你这那是PPT,就是浴皇大帝、昂科旗等车系......
  • JavaScript 获取元素的 坐标位置
         https://blog.csdn.net/im20166456/article/details/113793437?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefaul......
  • JavaScript中的函数
    函数:一个被设计为执行特定任务的代码块语法通过function关键词定义,后面跟着其函数名称,然后是一对圆括号,圆括号中可以定义一些函数的参数。没有名称的函数呢?函数名称......
  • 生成器 yield next
    介绍生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同function*gen{yiwld'一只没有耳朵';yiwld'一只没有尾巴'} 生成器函数其......
  • 浏览器中的JavaScript(2)
    2.2注册事件处理程序摘要:有两种事件处理程序的方式。第一种是web早期就有的,及设置作为事件的对象或文档元素的一个属性。第二种方法是把处理程序传给这个元素或对象或元......
  • 迭代器
    介绍迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator借口,就可以完成遍历操作。1.ES6创造了一种新的遍历命令for........
  • 关于 JavaScript 中柯里化函数的实现,附带详细解析!
    前言大家好,我是CoderBin,在面试当中,手撕代码的场景屡见不鲜,手写JS当中的方法更是最常见的一种,所以本文将全面的,详细解析柯里化函数的实现原理,并手写出自己的柯里化函数,相......
  • JavaScript如何做空字符串的校验
    通过正则表达式校验写法:1if(str&&/[^\s]/.test(str)){2//不为空3}else{4//为空5}注释:-正则表达式中\s是指空白,包括空格、换行、tab缩进等所有......
  • javascript 提取字符串方法 slice substr substring
    本文将对javascript提取字符串的三个方法slice/substr/substring,进行分析。这三个方法都具有提取字符串的功能,且都有两个参数。下面将详细介绍三个方法在一些特殊参数值......