几乎每份代码库里都能看到这种写法:
[...users].reverse().find(u => u.status === 'active')它能用,但每次都要把整个数组摊开成一份新副本,在原位置逆转顺序,再从头遍历,只为了拿到一个靠近末尾的元素。数据量一大,性能开销全花在毫无意义的复制上,接手的人还要花三秒才看懂意图——你其实只是想要“最后一个满足条件的值”而已。
这个问题在 ES2023 有了直接答案:findLast()和findLastIndex(),专门用来从数组尾部向前搜索,扔掉复制和反转。
findLast() 到底有多直接
它的回调签名和find()一模一样,也接收当前元素、索引和数组本身。区别只有一个:它从最后一个索引开始,向前推进。第一个让回调返回真值的元素,就是整个数组里“最后一个”符合条件的那个。
users.findLast(u => u.status === 'active')// 返回最后一位状态为活跃的用户,没有匹配则返回 undefined没有副本,没有原地反转,一趟逆向遍历就直接出结果,意图由方法名自己就表达清楚了。如果没有任何元素匹配,它和find()的行为一致——返回undefined。
const transactions = [{ id: 1, type: 'credit', amount: 100 },{ id: 2, type: 'debit', amount: 40 },{ id: 3, type: 'credit', amount: 200 },{ id: 4, type: 'debit', amount: 75 }transactions.findLast(t => t.type === 'credit')// { id: 3, type: 'credit', amount: 200 }上面的写法连变量声明都可以省掉,直接把表达式嵌进需要的地方。要的是“最近一笔入账”,就从尾部开始找,拿到的就是整张交易列表里最后那笔信用交易。旧写法要先复制、再反转、再find,三条操作链掩盖了核心意图。
需要的是位置,而不是值
findLast()返回元素本身。如果要拿到索引去做切片、拼接或者位置判断,就该findLastIndex()出场。
transactions.findLastIndex(t => t.type === 'credit')// 从最后一笔信用记录切到尾transactions.slice(transactions.findLastIndex(t => t.type === 'credit'))匹配不到任何元素时,它返回 -1,完全对齐findIndex()的习惯:transactions.findLastIndex(t => t.type === 'refund')直接得到 -1,不需要额外判断返回值类型。
三个会让你立刻想用的真实场景
审计日志与事件历史
你最常查的就是最近一次匹配特定条件的事件——最后一次登录失败、最后一次状态变更、最后一次在成功之前冒出来的错误。
auditLog.findLast(e => e.level === 'error')// 直接拿到对应错误对象,展示提示不用复制整条日志链再反转,方法名把“最后一条错误”的语义钉死在代码里。
撤销栈
找到最近一条可逆操作,无需重建整个栈:
history.findLast(action => action.undoable)// 返回最后一条可撤销的动作如果有上下文需要检查这条操作的索引,同样可以改写成findLastIndex()。
路径与面包屑
多层嵌套路径里找最后一级目录,或者面包屑数组里拿最深层级,findLast()让这种“要最后一项但带条件”的查询变得和读英文一样自然。
这两条方法已经落地在所有主流引擎里,Chrome 97+、Firefox 104+、Safari 15.4+ 以及 Node.js 18+ 都原生支持。下次再见到[...arr].reverse().find()这种模式,可以直接换成它们,顺便帮后来读代码的人省下一次脑内回溯。
热门跟贴