如何使用函数式风格实现JavaScript数组z-order插入
实现数组z序无突变插入的简洁方案
需求说明
需要实现数组元素插入逻辑,支持两种插入规则:
- 遵循插入顺序,直接将元素追加到数组末尾
- 按元素的
z属性升序排列插入(适配2D图形z-order绘制层级场景,要求z值相等时新元素插入到同z值元素的末尾,保证同层级后插入元素的绘制优先级)
原有命令式实现存在反向遍历、手动跳出循环、手动修正索引、拼接逻辑冗余的问题,需要更简洁的函数式无突变实现。
核心实现思路
不需要反向遍历,直接利用现有数组本身已按z值从大到小排序的特性(符合绘制顺序存储逻辑:z值越大越晚绘制,层级越高):
- 用
findIndex正向查找第一个z值小于待插入元素z值的索引,该位置就是正确插入点:- 若待插入元素z值比所有元素都大,会匹配到索引0,插入到数组最开头
- 若待插入元素z值和部分元素相等,同z元素的z值不满足「小于待插入z」的匹配条件,不会中断查找,最终插入点会落在所有同z元素之后
- 若所有元素z值都大于等于待插入元素z值,
findIndex返回-1,直接追加到数组末尾即可
- 无突变插入优先使用ES2023原生提供的
toSpliced()方法,该方法不会修改原数组,直接返回修改后的新数组,替代手动slice+展开运算符的冗余写法。
完整实现代码
const children = [ { z: 10 }, { z: 10 }, { z: 0 }, { z: 0 }, { z: -1 }, { z: -1 } ] /** * 无突变插入元素 * @param {object} item 待插入元素 * @param {Array} list 原元素数组(已按z值从大到小排序) * @param {boolean} sortByZ 是否按z序插入,传false则直接追加到末尾 * @returns {Array} 插入后的新数组 */ function insert(item, list, sortByZ = true) { // 不按z序时直接追加 if (!sortByZ) return [...list, item] // 定位插入点 const insertIndex = list.findIndex(el => el.z < item.z) // 所有元素z都大于等于当前元素,追加到末尾 if (insertIndex === -1) return [...list, item] // 原生无突变插入方法,兼容环境不支持时可替换为slice+展开运算符的写法 return list.toSpliced(insertIndex, 0, item) } // 测试用例 const testCases = [ { z: 7, name: 'new guy' }, // 插入到z=10元素后,z=0元素前 { z: 10, name: 'new guy' }, // 插入到原有z=10元素后,z=0元素前 { z: -1, name: 'new guy' }, // 插入到数组末尾 { z: 90, name: 'new guy' } // 插入到数组开头 ] testCases.forEach(testItem => { console.log(insert(testItem, children)) })
旧环境兼容写法
如果运行环境不支持toSpliced,可以将插入逻辑替换为以下写法,效果完全一致:
return [ ...list.slice(0, insertIndex), item, ...list.slice(insertIndex) ]
内容的提问来源于stack exchange,提问作者goodson
相关产品推荐
相关产品推荐

