Cytoscape.js 集合 every() 方法详解:全量条件校验与源码级剖析
【免费下载链接】cytoscape.jsGraph theory (network) library for visualisation and analysis项目地址: https://gitcode.com/gh_mirrors/cy/cytoscape.js
every()是 Cytoscape.js 中集合(collection)对象提供的全量谓词校验方法:它遍历集合中的每一个元素,只有当所有元素都满足回调函数(或条件)时才返回true。它在图数据校验、筛选前置判断、布局/样式批量操作前的条件检查等场景中非常实用,本文结合仓库源码与测试用例,深入讲解其用法、回调参数、边界行为以及与some()、allAre()等兄弟方法的异同。
一、核心用法与官方示例
eles.every( fn )对集合eles中的每个元素依次调用回调函数fn,只要有一个元素令fn返回假值(false),就立即返回false并停止遍历;只有全部元素都返回真值,最终才返回true。
原文档给出了如下示例(见 every.md):
var jAndE = cy.$('#j, #e'); var everyHeavierThan50 = jAndE.every(function( ele ){ return ele.data('weight') > 50; }); console.log( 'every heavier than 50 ? ' + everyHeavierThan50 );这里先用选择器'#j, #e'通过cy.$()取回 id 分别为j和e的两个元素组成集合,然后检查这两个元素的weight数据是否全部大于 50。只要其中一个元素的weight <= 50,结果即为false。
回调函数的三个参数
与原生数组的Array.prototype.every一致,Cytoscape.js 的回调会收到三个参数:
| 参数 | 含义 |
|---|---|
ele | 当前遍历到的元素(node 或 edge) |
i | 当前元素在集合中的索引(从 0 开始) |
eles | 正在遍历的整个集合对象 |
这一点有测试用例直接验证(见 collection-comparison.mjs):
it('eles.every()', function(){ expect( cy.edges().every(function( ele, i, eles ){ expect( is.elementOrCollection(ele) ).to.be.true; expect( is.elementOrCollection(eles) ).to.be.true; expect( is.number(i) ).to.be.true; return ele.data('source') === 'n1'; }) ).to.be.false; expect( cy.edges().every(function( ele, i, eles ){ expect( is.elementOrCollection(ele) ).to.be.true; expect( is.elementOrCollection(eles) ).to.be.true; expect( is.number(i) ).to.be.true; return ele.isEdge(); }) ).to.be.true; });该测试在beforeEach中初始化了 3 个节点(n1、n2、n3)和 2 条边(n1n2、n2n3):
- 第一条断言:因为并非所有边的
source都是n1(n2n3的 source 是n2),所以返回false; - 第二条断言:两条边全部满足
ele.isEdge(),所以返回true。
返回值语义
- 所有元素都满足条件 → 返回
true; - 任意一个元素不满足条件 → 立即短路返回
false; - 空集合→ 从实现逻辑看,循环体一次都不执行,直接返回
true(数学上的"全称命题对空集成立"语义),这是从源码结构可直接推断的行为。
二、源码级实现原理
every()定义在集合比较模块中,见 comparators.mjs:
every: function( fn, thisArg ){ for( let i = 0; i < this.length; i++ ){ let ret = !thisArg ? fn( this[ i ], i, this ) : fn.apply( thisArg, [ this[ i ], i, this ] ); if( !ret ){ return false; } } return true; },关键实现细节:
- 基于下标循环:Cytoscape.js 的集合对象继承了
Array.prototype(见 index.mjs),元素通过this[i]按下标直接访问,因此every()本质上就是对集合内部数组做一次线性扫描,时间复杂度为 O(n)(最坏情况)。 - 短路求值:一旦某个元素令回调返回假值,立即
return false,不再继续遍历,适合对大型图做快速失败校验。 - 可选的
thisArg:第二个参数用于绑定回调内部的this上下文。不传时直接以fn( this[i], i, this )形式调用;传入时通过fn.apply( thisArg, [ this[i], i, this ] )显式指定this。 - 回调返回值判定:使用
if( !ret )判断,因此回调返回0、null、undefined、''等任何假值时都会导致结果为false,返回任意真值则继续。
该模块通过util.extend( elesfn, props )挂载到集合原型(见 index.mjs),且由于元素(Element)与集合共享同一原型,单个元素同样可以直接调用ele.every(...)。
三、实用场景示例
1. 校验图中所有节点都已定位
var allPositioned = cy.nodes().every(function( node ){ return node.position().x != null && node.position().y != null; }); if( allPositioned ){ console.log('所有节点都有明确坐标,可以执行布局或导出'); }2. 校验边数据完整性后再运行图算法
var cy = window.cy; var edgesReady = cy.edges().every(function( edge ){ return typeof edge.data('weight') === 'number' && edge.data('weight') > 0; }); if( edgesReady ){ var result = cy.elements().dijkstra( { root: '#n1', weight: function( ele ){ return ele.data('weight'); } } ); // 继续使用最短路径结果…… }3. 与选择器结合,先取子集再校验
var selectedNodes = cy.$('node:selected'); var allSelectedLocked = selectedNodes.every(function( node ){ return node.locked(); }); if( allSelectedLocked ){ console.log('所有选中节点都处于锁定状态'); }四、与some()、allAre()等方法的对比
在同一个 comparators.mjs 模块中,还定义了语义互补的系列方法:
| 方法 | 语义 | 返回true的条件 | 说明 |
|---|---|---|---|
every( fn ) | 全量满足 | 所有元素满足fn | 本文主角,短路于首个失败元素 |
some( fn ) | 存在满足 | 至少一个元素满足fn | 短路于首个成功元素,见 some.md |
allAre( selector ) | 全量匹配选择器 | 所有元素都匹配选择器 | 基于every()实现:return this.every(function( ele ){ return selObj.matches( ele ); })(见 comparators.mjs),见 allAre.md |
is( selector ) | 存在匹配选择器 | 至少一个元素匹配选择器 | 基于some()实现(见 comparators.mjs),见 is.md |
从源码可以清晰地看到,allAre是"用选择器写法的 every",is是"用选择器写法的 some"。选择器版本更简洁,但灵活性低于回调版本——回调可以访问元素数据(ele.data())、坐标(ele.position())、样式(ele.style())等运行时信息,而选择器只能基于既有的数据字段、类名、状态等结构化属性。
典型组合用法:
cy.elements().every(...)判断全图;cy.$('#a, #b').some(...)判断子集。例如在 collection-comparison.mjs 的allAre测试中,cy.$('#n1, #n2').allAre('node')为true,而cy.$('#n1, #n1n2').allAre('node')因混入了边而返回false——这与every的"全量满足"语义完全一致。
五、注意事项与最佳实践
- 条件函数中的
return不要遗漏:every()依赖回调返回值做判断,回调体若漏写return会返回undefined(假值),导致结果恒为false。 - 空集合返回
true:对cy.collection()等空集合调用every()会直接得到true,在业务逻辑中若需要"非空且全部满足",应先判断eles.length > 0或使用eles.nonempty()。 - 避免在回调中修改集合:遍历期间修改集合元素可能导致索引错位,建议先用
eles.filter()或eles.remove()等操作生成新集合后再校验。 thisArg用法:当回调是对象方法时,可用eles.every( obj.check, obj )形式绑定this,便于复用校验逻辑。- 性能:
every与some均具备短路特性,在校验大型图时比"先filter()再比长度"的方式更高效,因为失败时不必扫描完整个集合。
结语
every()是 Cytoscape.js 集合 API 中最基础也最常用的"全量校验"入口,其实现简洁、语义清晰,并与some()、allAre()、is()构成完整的谓词校验矩阵。无论是做数据完整性检查、图算法前置条件判断,还是交互场景中的状态汇总,它都能以可读、可维护的方式表达"是否全部满足"这一逻辑。更多集合方法与示例可参考 collection 文档目录 及对应的单元测试 collection-comparison.mjs。
【免费下载链接】cytoscape.jsGraph theory (network) library for visualisation and analysis项目地址: https://gitcode.com/gh_mirrors/cy/cytoscape.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考