集合迭代任务与iterator对象技术

ES6新增iterator技术是被高度关注的技术,然,iterator确切是什么技术,解决何种编程任务(方便我们制作什么样的程序功能),又与 generator 技术(generator函数返一个iterator对象实例)是何关系,是一系列相关的重要的问题。

集合迭代算法

MDN文档开始劈头一句道出了iterator技术针对的任务:集合元素(项)的处理;并且强调这种任务非常的常见:

Processing each of the items in a collection is a very common operation.

集合迭代算法/任务

要认识iterator技术,必先体会「集合迭代算法/任务」的实质,和意义。

利用统一迭代行为协议制作「可迭代对象」数据源,和利使用「可迭代对象」(通用for of等操作)制作有迭代结构的算法程序,是两个不同的编程任务;所以iterator技术要 分两个方面讲。

迭代操作

什么是迭代?迭代(iterate)原义是指同种操作反复,直达一个终结条件的出现;在编程的语境上,迭代则是针对集合数据一个种处理——逐个遍历访问集合所有数据项。就像每种「数据类型」有特定的操作(计算),如数值类型有算术操作,迭代操作 可看作是【集合】的特定「操作」。

集合类型常见是线性索引数组(indexed array)和带键标识的键值数组(associated array)。

为什么迭代

编程的任务注1是构建具有某种计算/算法结构的功能程序,很多计算功能是基于集合数据结构的,要遍历集合数据项,例如数值数组求和,字符组转换等。简单说,很多计算功能(的算法实现)具有迭代结构。

迭代实例

We have lots of places where we get a collection of items—consider document.querySelectorAll, which returns a NodeList, or Immutable.js 1 which has an Immutable.List object. We currently have no canonical API that we reach for to look at all the items in these collections. (此处需要进一步润色)

三代迭代技术(for forEach iterator)

有了迭代计算结构的(任务)需求,就有相应的迭代技术。最早的迭代技术是所有程序员都熟悉的数组for循环(while循环原则也算,只是语法上没有for循环“甜”注2,也算是手动迭代技术,只是一种变相)。然而,for循环的弊病是明显——要手动管理迭代细节,噪音太多,很不“甜”。很多语言都发现传统「for循环」的不便,引入更易用更“甜”的自动迭代器工具——iterator。

就JS而言,ES5引入了array.forEach方法,和for...in操作,然,forEach方法只适用于数组,for...in操作的功能也有局限。随着JS应用规模的增长,更自动更“甜”的迭代技术——iterable object——被引入ES6。

迭代技术是一种编程技术,具体何为编程技术/工具?从最原初的迭代技术看,它由for循环语句,和array的长度(length)两者,才能完成迭代任务,由此可具体知晓所谓「编程技术」何所指。

集合迭代编程

整个「JS集合迭代编程」涉及两个层面,多个角度。

  • 第一个层面,是使用可迭代数据源(iterable object ),进行具体的集合迭代功能的程序开发(特征是使用了for循环);
  • 第二个层面,是根据迭代协议,制作可迭代数据源(特征是制作具有迭代操作API的对象);

角度则有抽象的迭代协议、迭代操作符和可迭代对象。

EM: 有趣的是,使用函数对象技术(轻便可作值传递)的「异步编程」也有多个层面和角度,例如事件模型、对象生命模型。

迭代协议

像JAVA这样的OO语言,通用的迭代操作行为可用一个抽象基类(interface)来统一定义,具体特定的迭代对象则对其进行派生。JS没有抽象基类,迭代行为由语言构件之外的语言标准来约定——迭代协议(iteration protocals)。迭代协议由「可迭代性」和「迭代操作」两部分组成。

可迭代性

一个对象是否是标准的可迭代数据源,有一个唯一的属性标识——Symbol.iteratror,适用于迭代操作的安全标识。[Symbol.iteratror]是一个对象方法属性,返回一个实现了「迭代操作协议」的迭代器对象。

迭代器

协议规定了迭代操作的通用API行为:

有下一个值吗?是多少?

所以迭代器的结构很简单:一个next方法,方法返回一个结果{done,value},回答:

有下一值?(next),如果有(done),是多少(value)。

通过这个协议,「迭代数据源制作者」清楚如何制作 iterable object ,制作「具有迭代算法结构的程序」的开发者知道如何使用 iterable object (除了手动迭代,迭代操作符 for of等可自动代劳)。

可迭代对象

可迭代对象( iterable object )是「JS集合迭代编程」的核心,作为一种「计算对象」,迭代协议统一定义了它的通用结构和行为;数组(array)是原始的可迭代对象,ES6迭代技术将迭代操作泛化到一切对象。认识可迭代对象的一个难点,是区分「迭代器」和「可迭代对象」的关系。

迭代器和可迭代对象

迭代器协议是抽象的,它只规定了迭代API的形式涵义,而具体的迭代算法取决具体可迭代数据源逻辑;简单的说next方法返回下一个值,而这个值怎么来的,则是 iterable object A特定的。要制作一个自定义的可迭代对象(将一个普通对象转换为可迭代对象)就是为其制作特定的迭代器——决定每次迭代返回的值,不存独立的迭代器对象。

跟后补充:由于不存独立的 iterator(iterator必须附属在一个父对象上使用),故 iterator 和 iterable可概念互替,二者在字眼上表征了 iteration不同侧面;再,iterator 不像array是一种数据类型,它更像是表征了一次集合的动态使用,从集合迭代算法结构抽象出来的一次性数据。

内建和自定的

String, Array, TypedArray, Map and Set are all built-in iterables your iterable object (此处需要进一步润色)

迭代操作符

有了可迭代对象作数据源,我们就可迭代「使用它」,我们可手动迭代(调用next方法),然主要通过JS提供的自动迭操作符,包括for ..of 和...spread操作符,简化制作「具有迭代结构」的程序功能。

自定义的可迭代对象与定制铁锤

通过以上简精的分析,所谓「迭代技术」不是一单一的新工具/技术,不是单一的新型铁锤,也不像是新的CSS布局技术(flexbox)那样单一。「迭代技术」可涵盖多个方面。看这个图:

开发者集合迭代编程的「任务」只是制作「集合迭代结构的程序」,使用的「技术」是一个易用的「可迭代对象」和JS提供的自动迭代操作符(例如for of)。

除以上外,JS还提供一些内建的「可迭代对象」,和迭代协议给开发者转变角色,自行开发「可迭代对象」。从这个角度看,JS提供的不(仅)是一个现成的铁锤(当然内建的可迭代对象算是现成的),而是一张设计图。开发者除了使用铁锤,还可以自己煅造特制铁锤。

Generator 生成函数对象

迭代器/可迭代对象本身也是有结构的,也是一种「程序」,有自己的「编程任务」,也需要「技术」。上面提到的迭代器制作,是手动(的参照迭代器协议),不使用任何中间技术/工具。由于手动被认为过于繁琐,需要一些工具,Generator函数就是这样的工具/技术。

那,迭代器的结构是怎样的呢?Generator函数的什么功能特性满足了「制作迭代器任务」的高效率呢?这都涉及了Generator函数到底是什么技术,有什么功能特性(例如G函数对象有暂停的功能),解决何种编程任务。

小结

所谓「集合迭代任务」是指程序员制作带有(对集合)迭代算法操作的功能程序,使用for循环是其重要特征,而为了方便制作这种程序,ES6提供一个统一的「工具」——iterator技术,iterator技术包括以下几个方面:

  • 第一,自动迭代操作符,for...of 展开操作 等;
  • 第二,现成的可迭代对象(iterable),例如array;
  • 第三,抽象的迭代协议,供自定义可迭代对象使用;
  • 第四,更方便的 iterable 制作的工具——G函数。

  1. 某人的「任务」就是按自然律构建某种「存在」。 算法步骤本身也是有特性的,集合迭代是比较常的算法特征;另一个常见算法现象就是异步算法。
  2. “甜”是指语法糖果,越“甜”的语法越"适应"人脑思维特性,便于推理,提高编程效率。
裸男
Nakeman.cn 2023 Build by Gatsby and Tailwind, Deploy on Netlify.