广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript可迭代对象详细介绍
  • 396
分享到

JavaScript可迭代对象详细介绍

2024-04-02 19:04:59 396人浏览 八月长安
摘要

目录1、迭代器2、迭代器接口与可迭代对象3、自定义可迭代对象3.1、可迭代的Range对象3.2、使用Generator函数作为迭代器接口3.3、可迭代的List3.3、可迭代的迭代

1、迭代器

迭代器是借鉴c++等语言的概念,迭代器的原理就像指针一样,它指向数据集合中的某个元素,你可以获取它指向的元素,也可以移动它以获取其它元素。迭代器类似于数组中下标的拓展,各种数据结构,如链表(List)、集合(Set)、映射(Map)都有与之对应的迭代器。

js中的迭代器是专门为了遍历这一操作设计的。每次获取到的迭代器总是初始指向第一个元素,并且迭代器只有next()一种行为,直到获取到数据集的最后一个元素。我们无法灵活移动迭代器的位置,所以,迭代器的任务,是按某种次序遍历数据集中的元素。

JS规定,迭代器必须实现next()接口,它应该返回当前元素并将迭代器指向下一个元素,返回的对象格式为{value:元素值, done:是否遍历结束},其中,done是一个布尔值。done属性为true的时候,我们默认不会去读取value, 所以最后返回的经常是{value: undifined, done: true},注意,返回类似{value: 2, done: true} 不会导致错误,但是因为done设置为true,在for...of等操作中都会忽略value的值。因此,done:falsevalue:undifined可以省略。一个简单的JS迭代器像这样:

let iter = {
    i: 0,
    next() {
        if (this.i > 10) return { done: true };
        return { value: this.i++ };
    }
}
//手动使用迭代器
console.log(iter.next());  //{ value: 0 }
console.log(iter.next());   //{ value: 1 }
while (true) {     
    let item = iter.next();
    if (!item.done) {
        console.log(item.value);     //打印从2到10
    } else {
        break;
    }
}

可以看到,迭代器与普通的JS对象没有区别,它就是一个用于实现迭代的对象。手动操作迭代器并没有什么实用性,迭代器的作用是附着在对象上,让一个对象,或者数据结构成为可迭代对象。

2、迭代器接口与可迭代对象

迭代器接口是我们获取对象迭代器时默认调用的接口,一个实现了迭代接口的对象即是可迭代对象。JS的默认迭代接口是[Symbol.iterator], 一个对象实现了[Symbol.iterator]接口就成为了可迭代对象。

[Symbol.iterator]是一个特殊的Symbol属性,它用于JS内部检测一个对象是否为可迭代对象。接口一词的含义代表它是一个函数,其结果应该放回一个迭代器。结合上面迭代器必须要有next()操作,所以,对可迭代对象,调用链iterableObj[Symbol.iterator]().next()应该是可行的。数组是最具代表性的可迭代对象,让我们拿数组测试一下:

arr = [1, '2', {a: 3}];
let arrIt = arr[Symbol.iterator]();    //获取数组迭代器
console.log(arrIt.next());   //{ value: 1, done: false }
console.log(arrIt.next());  //{ value: '2', done: false }
console.log(arrIt.next());   //{ value: { a: 3 }, done: false }
console.log(arrIt.next());  //{ value: undefined, done: true }

可以看到,迭代器的next()接口确实如愿工作,并且返回上述的结构。

3、自定义可迭代对象

现在,让我们来实现几个可迭代对象,这十分简单,只要:

  • 实现对象的迭代器接口[Symbol.iterator](),注意它是一个方法,
  • 在迭代器接口中返回一个迭代器对象,
  • 确保迭代器对象具有next()接口,并且返回{value: v, done: bool}的结构。

3.1、可迭代的Range对象

作为第一个可迭代对象,我们来实现类似python中的range(from, to),不过这里使用Range对象来封装一个左闭右开的范围[from, to)

function Range(from, to) {
    this.from = from;
    this.to = to;
}
Range.prototype[Symbol.iterator] = function () {
    //返回一个迭代器对象
    return {
        cur: this.from,
        to: this.to, //保证next()中可以获取
        next() {
            return (this.cur < this.to) ? {
                value: this.cur++,
                done: false
            } : {
                value: undefined,
                done: true
            };
        }
    }
}
let range = new Range(5, 11);  //创建一个range对象
//使用for...of循环
for (const num of range) {
    console.log(num);    //依次打印5,6,7,8,9,10
}
//使用
let arrFromRange = Array.from(range);
console.log(arrFromRange);   //[5,6,7,8,9,10]

3.2、使用Generator函数作为迭代器接口

因为Generator函数产生的generator对象是一种特殊的迭代器,所以我们可以很方法地使用Generator函数作为对象的迭代器接口。使用Generator函数改写上面的迭代器接口:

Range.prototype[Symbol.iterator] = function* () {
    for (let i = this.from; i < this.to; i++) {
        yield i;
    }
}

这种写法更加简洁易懂,是最为推荐的写法,Generator函数中产生的值就是遍历过程中得到的值。

3.3、可迭代的List

接下来,我们自定义一个链表节点List,在此我们省去不必要的接口。

function Listnode(value) {
    this.value = value;
    this.nextNode = null;
}

function List(root) {
    this.cur = this.root = root;
}
//List的next接口
List.prototype.next = function () {
    if (this.cur) { //非尾哨兵节点
        let curNode = {
            value: this.cur.value
        };
        this.cur = this.cur.nextNode;
        return curNode;
    } else {
        return {
            done: true
        };
    }
}

List.next()实现了将链表指针后移的操纵,并且返回了移动前节点的值,你可能注意到,我特意让返回的对象格式与迭代器返回结果一致,下面你将看到这么做的原因。现在我们让List变成可迭代,按照之前的做法,使得List[Symbol.iterator]().next()能够返回正确的{value: v, done: true}格式。是的,我们已经画好龙了,就差一个点睛之笔:

List.prototype[Symbol.iterator] = function () {
    return this;
}

随手写一个测试:

let a = new ListNode('a'),
    b = new ListNode('b'),
    c = new ListNode('c');
a.nextNode = b, b.nextNode = c;
let list = new List(a);
for (let i of list) {
    console.log(i);  //a,b,c
}

Perfect! List的迭代器接口返回了它自己,利用了自身的next()接口完成迭代操作,也就是说List的迭代器是List本身,我都为自己构思的例子觉得巧妙。

3.3、可迭代的迭代器

上面的List例子会让人觉得有点牵强,list.next()的返回值为了迎合迭代器的要求,让平时不得不使用let curValue = list.next().value来正确接收返回的节点值,确实。但是,这种做法在一种时候让人觉得眼前一亮——让迭代器称为可迭代对象,因为自己就是可迭代器,让自己成为自己的迭代器,就像1=1一样正确自然。

回到开头埋下的雷,我们只需要稍加改动

let iter = {
    i: 0,
    next() {
        if (this.i > 10) return { done: true };
        return { value: this.i++ };
    },
    //让迭代器的迭代器接口返回自身
     [Symbol.iterator]() {
        return this;
    }
}
//这样,你就可以把迭代器用在任何可迭代对象的地方
for (let i of iter) {
    console.log(i);  
}

这样,这个迭代器本身也是可迭代的。需要注意的是,内置可迭代类型的迭代器也都是可迭代的,类似for(let i of arr[Symbol.iterator]())的操作是可行,其原理是让Array的迭代器继承Array.prototype。其它类型也有类似的继承,如Generator与generator对象。

4、可迭代对象的意义

可迭代对象作为数组的扩充,具有非凡的意义。在以前,对一个需要操作一组数据的接口,只有数组这种结构能支持,非数组对象必须通过某种方式转化为数组,完成之后,还可能需要还原成原来的结构,这种繁琐的来回切换很不理想。有了可迭代对象的概念,这类操作可以接受一个可迭代对象,数组是可迭代对象,所以之前的数组参数是仍然可行的,在此之上,任何实现了可迭代接口的对象,也能正常处理。考虑这个下面例子:

function sumInArray(arr){
    let sum=0;
    for(let i = 0;i<arr.length;i++){
        sum+=arr[i];
    }
    return sum;
}
function sumInIterable(iterable){
    let sum = 0;
    for(let num of iterable){
        sum+=num;
    }
    return sum;
}

sumInArray()只对数组友好,而sumInIterable()是所有可迭代对象通用的,例如我们前面的Range对象,iter对象。是的,数组到可迭代对象的提升,代表了接口的通用性的提升。这个道理太浅显易懂,以至于你可能觉得我说废话,那么,请问你在接口设计的时候,会考虑能否使用可迭代对象代替数组吗?个人认为这种提升很多时候是有益的,特别在一些应用场景较多的接口,我发现很多es6操作也是基于可迭代对象。如果有什么看法,也欢迎评论区探讨。

5、使用可迭代对象

先认识JS内建的可迭代对象:

  • 非weak的数据结构,包括Array,Set, Map
  • DOM中的NodeList对象
  • String对象
  • 函数的arguments属性

再了解哪些操作是基于可迭代对象的:

  • for...of语法
  • ...iterable:展开语法和解构赋值
  • yield*语法
  • Map, Set, WeakMap,WeakSet的构造器。为什么没有Array?因为Array直接把它对象当成元素了,但是有Array.from(iterable)
  • Object.fromEntries(iterable) ,每次迭代结果应该是一个对应键值对的二元数组,与Map的迭代结果吻合,常有let obj = Object.fromEntries(map)实现从map到object的转化。
  • promise.all(iterable)promist.race(iterable).

我认为对这些方法的具体使用不该放在这里,如果使用过它们,自然了解,只需要记住它们对任何可迭代对象都是支持的。如果不认识它们我也说不完,你应该一一学习去。

6、后记

到此这篇关于javascript可迭代对象详细介绍的文章就介绍到这了,更多相关JS可迭代对象内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaScript可迭代对象详细介绍

本文链接: https://www.lsjlt.com/news/151029.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript可迭代对象详细介绍
    目录1、迭代器2、迭代器接口与可迭代对象3、自定义可迭代对象3.1、可迭代的Range对象3.2、使用Generator函数作为迭代器接口3.3、可迭代的List3.3、可迭代的迭代...
    99+
    2022-11-13
  • JavaScript内置对象Math与String详细介绍
    目录Math对象Math获取随机数String对象Math对象 js内置数学对象 不是一个构造函数 所以不需要使用new来调用 而是直接使用里面的属性和方法即可 Math.PI 圆周...
    99+
    2022-11-13
  • Python 类和对象详细介绍
    目录对象 = 属性 + 方法self是什么公有和私有继承调用未绑定的父类方法使用super函数多重继承组合构造和析构_ _init_ _(self[, …])构造方法_...
    99+
    2022-11-11
  • Python中可变和不可变对象的详细介绍
    这篇文章主要介绍“Python中可变和不可变对象的详细介绍”,在日常操作中,相信很多人在Python中可变和不可变对象的详细介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python中可变和不可变对象的详...
    99+
    2023-06-20
  • JavaScript原始值与包装对象的详细介绍
    目录前言正文原始类型 (Primitive types)原始值 (Primitive values)包装对象 (Wrapper objects)对象 (Object)构造函数 (Co...
    99+
    2022-11-12
  • java——面向对象详细介绍(三)
    本文接上文:java——面向对象详细介绍(二)11、接口(interface)引入:抽象类是从多个类中抽象出来的模板,若要将这种抽象进行得更彻底,就得用到一种特殊的“抽象类”→ 接口;例子:生活中听说过的USB接口其实并不是我们所看到的那些...
    99+
    2022-03-09
    java教程 面向对象
  • java———面向对象详细介绍(二)
    本文接上篇文章:java——面向对象详细介绍(一)11、子类访问父类和方法覆写子类不能直接访问父类的私有成员;但是子类可以调用父类中的非私有方法来间接访问父类的私有成员。Person类中有私有字段name,Student继承Personne...
    99+
    2015-10-13
    java教程 面向对象
  • Android中的Looper对象详细介绍
    Java 官网对Looper对象的说明: public class Looperextends ObjectClass used to run a message loop f...
    99+
    2022-06-06
    looper Android
  • java——面向对象详细介绍(一)
    1、什么叫面向对象?面向对象(Object-Oriented,简称OO)就是一种常见的程序结构设计方法。面向对象思想的基础是将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的基础。面向对象是一个很...
    99+
    2020-10-09
    java教程 面向对象
  • Java递归和迭代区别详细介绍
    目录1.递归和迭代的区别2.代码1.递归和迭代的区别 当实体调用自身时,程序称为递归。当存在循环(或重复)时,程序称为迭代调用。示例:求一个数的阶乘的程序  时间复杂度比较...
    99+
    2023-05-15
    Java 递归和迭代区别介绍 Java递归和迭代的区别 递归和迭代的区别
  • JavaScript可视化与Echarts详细介绍
    目录一、可视化介绍二、可视化库介绍三、EchartsEcharts引入和使用了解基础配置一、可视化介绍 可视化:将数据用图表展示出来,让数据更加直观、让数据特点更加突出应用场景:营销...
    99+
    2022-11-13
  • java中关于对象的详细介绍
    一、对象的创建步骤:(1)声名对象变量:对象变量的声明并没有创建对象,系统只是为该改变量分配一个引用空间。(2)对象的实例化:为对象分配空间,执行new运算符后的构造方法完成对象的初始化,并返回该对象的引用。过程:首先为对象分配内存空间,并...
    99+
    2014-08-23
    java入门 java 对象
  • Java对象与Java类的详细介绍
    本篇内容介绍了“Java对象与Java类的详细介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录面向对象是什么Java类什么是类Java...
    99+
    2023-06-20
  • java面向对象——多态的详细介绍
    一、概述多态是继封装、继承之后,面向对象的第三大特性。生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是...
    99+
    2020-05-09
    java入门 java 面向对象 多态
  • Java对象的内存布局详细介绍
    目录一、对象头1)、Mark Word2)、类型指针3)、数组长度(只有数组对象才有)二、实例数据三、对齐填充四、使用JOL工具分析对象内存布局在HotSpot虚拟机中,对象在内存中...
    99+
    2023-02-13
    Java对象内存布局 Java内存布局 Java对象内存
  • JavaScript中的迭代器和可迭代对象与生成器
    目录1. 什么是迭代器?1.1 迭代器的基本实现1.2 迭代器的封装实现2. 什么是可迭代对象2.1 原生可迭代对象(JS内置)2.1.1 部分for of 演示2.1.2 查看内置...
    99+
    2022-11-13
  • 详解Python之可迭代对象,迭代器和生成器
    目录一、概念描述二、序列的可迭代性三、经典的迭代器模式四、生成器也是迭代器五、实现惰性迭代器六、使用生成器表达式简化惰性迭代器总结 一、概念描述 可迭代对象就是可以迭代的对象,我们可...
    99+
    2022-11-12
  • 关于java中类和对象的详细介绍
    类和对象对象我们知道,代表现实世界中可以明确标识的一个实体(万物皆对象),每个对象都有自己独特的标识、状态和行为。类是具有相似特征和行为的事物的统称。使用一个通用类来定义同一类型的对象。 类是一个模板 、蓝本或者说是合约 , 用来定义对象的...
    99+
    2015-06-07
    java入门 java 对象
  • JavaScript中可迭代对象与迭代器的作用是什么
    今天就跟大家聊聊有关JavaScript中可迭代对象与迭代器的作用是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Lazy evaluation...
    99+
    2022-10-19
  • JavaScript Lazy evaluation中可迭代对象与迭代器是怎样的
    今天就跟大家聊聊有关JavaScript Lazy evaluation中可迭代对象与迭代器是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。L...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作