数据表格中的迭代器
DataTables 从根本上运行重复数据。作为开发者,我们当然想要访问和处理这些数据,一次处理一个条目或以某种方式操作它们。我们称之为对这些条目进行迭代或循环。DataTables 有许多内置的迭代方法,但如果你刚接触 DataTables API,不一定明白应该使用哪种方法。
在本文中,我想花一些时间深入探究 API 并解释 DataTables 中内置的迭代器,以及何时最好使用它们。我还将讨论使用每个迭代器是否有任何惩罚(提示 - 是的,易用性和性能通常需要权衡取舍)。
DataTables 中有三个主要的迭代器
rows().every()
、columns().every()
以及cells().every()
- 这些迭代器组合在一起,因为它们都具有相同的基本行为。each()
iterator()
还有一些帮助器方法,它们可以让你更轻松地从重复的数组中获取数据,例如 reduce()
和 map()
。这些内容将在文章最后探讨,但深度较浅。
基本原理
DataTables API 在本质上类似数组,也就是说它看起来非常像 Javascript 数组。它有一个 length
属性、 slice()
和 push()
方法,这些方法你通常都会将它们与数组关联在一起 - 实际上,它使用了 JavaScript 中内置的大量数组函数;你甚至可以使用 for
循环来迭代它,但它实际上并不是一个数组。这是一个类 (DataTables.Api
) 的实例,它的外观和行为都非常像一个数组。
在讨论迭代方法时,重要的是要牢记上面介绍的内容,因为这是 DataTables 及其 API 如何工作的关键(以及 链式调用 的概念)。
核心迭代方法
你应该使用的迭代方法取决于你要实际执行的表操作或数据检索。简单总结一下
rows().every()
、columns().every()
以及cells().every()
- 在你想要对每个选定条目执行 API 方法时使用它们,但会有一定性能损失。each()
- 不是行、列或单元格的任何数据类型的迭代器(也就是说,用于 DOM 元素和数据)iterator()
- 较低级别的 API,但会带来一个巨大的开发开销。
*.every 方法
您可能早已注意到,*.every()
方法总是显示为链接到表项访问 API 方法(rows().every()
、columns().every()
和 cells().every()
)之一,而不是仅描述为 every()
,就像 each()
和其他实用程序方法。
每个 *.every()
方法的实际操作取决于使用的项访问方法,具体取决于它们如何执行以及向它们传入的参数。它们提供了一种简单的方法,可在该对象的范围内针对所选的每个项访问 DataTables API 对象。
我们用一个示例来说明:您想在表中的每一行上使用 row().child.show()
,以使所有子行可见。该方法没有复数形式,因此您需要依次访问每一行并调用该方法。您可能很想做类似的事情
var nodes = table.rows().nodes();
for ( var i=0, ien=nodes.length ; i<ien ; i++ ) {
table.row( nodes[i] ).child.show();
}
这将奏效,但非常麻烦,因为您需要选择所有行,遍历它们,选择每一行,然后对它们执行所需的运算。
*.every()
方法会自动将内部函数的范围更改为该项的 DataTable API 实例。这意味着您可以在上面中使用 this
代替 table.row(...)
- 换句话说,它是为您完成的选择。因此,我们可以将上面内容重写为
table.rows().every( {
this.child.show();
} );
更容易阅读,简洁得多!
这对
和 columns()
方法同样有效,但不能用于 API 使用的任何其他数据类型。例如,您无法使用 cells()
rows().data().every()
- *.every()
函数的全部目的是为了方便访问该项的 API 实例,而这对其他数据类型来说不是有效的事情。
现在转向缺点 - 由于上下文切换,*.every()
方法成为 DataTables 中最慢的迭代器。必须为每个项创建一个新的 API 实例,并将函数上下文切换到该实例。尽管这在现代浏览器上速度很快,但在大数据集上仍然会很明显。因此,当需要最大性能时,不应使用这些方法(例如,拖放事件)。然而,它们的使用方便性不可低估,并且应该在大多数情况下(例如,单击事件)使用它们。
each() 方法
对于*.every()
方法无法处理的地方,each()
方法可以处理!*.every()
的一个限制是,它只能与上述提到的三个复数项选择器方法连接。对于 DataTables API 可以承载的所有其他数据类型,我们使用 each()
来访问实例中的数据。
许多人会熟悉 jQuery.each
方法,该方法可以用于迭代对象和数组 - DataTables 自己的 each()
方法基本上是相同的:对于实例中的每一项调用一次回调函数,允许对其进行操作
table
.rows()
.nodes()
.each( function( value, index, api ) {
$( value ).addClass( 'loopy' );
} );
在这种情况下,我们使用 rows().nodes()
获取表中的所有行节点(
元素),然后遍历它们,使用 jQuery 为每个元素添加一个类。tr
这个迭代方法非常快,因为它不需要在每次执行回调函数时创建一个新的 API 实例。话虽这么说,它仍然不如传统的 for
循环快。如果在代码的关键性能区域中,上面可以写成以下内容
var nodes = table.rows().nodes();
for ( var i=0, ien=nodes.length ; i<ien ; i++ ) {
$( nodes[i] ).addClass( 'loopy' );
}
需要指出的是,上面只是为了演示代码 - jQuery 已经内置了非常好的数组处理(毕竟它也是类数组),我们可以直接将节点数组传递给 jQuery。我们还可以使用 to$()
方法将 DataTables API 实例转换为 jQuery 实例
table.rows().nodes().to$().addClass( 'loopy' );
iterator() 方法
方法与 iterator()
*.every()
方法类似,因为它可用于访问表中的项信息。但在这种情况下,它不会创建一个新的 API 实例并且不会更改回调函数的作用域。相反,它只提供一个索引到 DataTables 的内部数据存储中的项 - settings()
。
这就是事情变得有点棘手的地方。如果您查看 settings()
文档,您会注意到强烈建议您不要使用它!settings 对象中的信息被认为是非公开 API,并且参数名称可以在版本之间更改,而无需警告。但是,如果不提 iterator()
方法,关于 DataTables 中迭代器的任何讨论都将严重不足。
iterator()
方法的主要优点在于它的速度很快。它是一个简单的循环,带有用于查找数据的回调函数和索引。它被 DataTables 提供的 API 方法使用,如果您编写的代码必须在微观层面表现得非常好,这就是您的做法。
我强烈建议任何使用 iterator()
的方法都应该写在 自定义 API 方法 中,因此如果您确实访问了需要在版本之间更改的任何内部信息,您只需更改一个点。通常,在“用户空间”中访问表项时,最好坚持使用 *.every()
方法!
实用方法
虽然上述讨论涵盖了 DataTables 中最常用的迭代方法,但还有一些其他值得强调的方法
map()
与 each()
方法(以及它的 jQuery 表亲)非常相似,map()
方法用于从另一个数组创建一个数组。例如,考虑我们有一个对象数组,我们想要从这些对象中提取一个数据点,例如一个 totalCost
参数
var totalCosts = table
.rows()
.data()
.map( function ( data ) {
return data.totalCost;
} );
pluck()
对上述操作的需求并不罕见——事实上,它被称为“提取数据”,而 DataTables 提供了一个实用方法来轻松地从对象中提取单个数据点:pluck()
。有了此方法,我们可将上述内容写成
var totalCosts = table.rows().data().pluck( 'totalCost' );
值得注意的是,尽管 pluck()
可用于访问单个数据点,但不能用于构建更复杂的数据对象。它也不能用于访问嵌套数据。对于这种情况,map()
方法仍然派得上用场!
reduce()
reduce()
方法用于将数组转换成一个单一的标量值。它通常用于在 DataTable 中进行求和计算,尽管你也可以将它用于需要将数据集简化为单个值的任何其他计算。继续我们 totalCost
的示例,如果我们想要对该值求和,我们可以使用
var totalCost = table
.rows()
.data()
.pluck( 'totalCost' )
.reduce( function ( a, b ) {
return a + b;
}, 0 );
toArray() 和 to$()
尽管 DataTables API 实例非常有用并且提供了许多实用方法,但有时你可能只需要一个简单的数组数据(例如:在 JSON.stringify()
它时),或者你想要将 DataTables API 实例转换成为一个 jQuery 对象以便可以使用 jQuery API(通常在使用 rows().nodes()
、column().nodes()
或 cells().nodes()
时)。
var data = table.rows().data();
JSON.stringify( data );
unique()
最后,在这次讨论中还有 unique()
方法。它可以非常简单地获取一个结果集,并移除其中的任何重复值
table
.rows()
.data()
.pluck( 'lastName' )
.unique();
值得强调的是,DataTables API 并没有尝试复制帮助程序库的全部功能,比如 Underscore 或 Lodash,用于数组操作。这里提供的实用方法是在使用数据表时最有用的一些方法。如果你确实想要使用这些库的更复杂方法,toArray()
可用于将数据转换成一个简单数组。
结论
没有通用的 DataTables 迭代器,您必须始终使用它。相反,您会发现需要使用各种可用的方法,根据您正在处理的数据以及您想要执行的操作来选择使用哪种方法。希望本文会帮助阐明何时使用每种方法。
这是一个非常技术性和基于理论的帖子 - 我将在六月回到实践中!