链式调用需手动实现因JS原生方法默认不返回this;核心是每个方法末尾return this,终止方法如get()返回结果;易错点包括异步断裂、this丢失、混用返回类型及调试困难。
JavaScript 的链式调用本身不提升可读性,它只是让代码「看起来更紧凑」;真正提升可读性的,是设计得当的 API 语义 + 每个方法返回 this 或新的上下文对象。
JS 原生函数(包括数组方法如 map、filter)默认不返回 this,而是返回新数组或新值。要链式调用,必须显式返回对象自身(或封装后的实例)。
Array.prototype.push() 返回数组长度(不是数组),所以 [1].push(2).push(3) 报错:TypeError: Cannot read property 'push' of undefined
Array.prototype.map() 返回新数组,能链式,但它是「函数式链式」,和「面向对象风格链式」目的不同:前者强调不可变,后者常用于配置或构建流程核心就一条:所有公开方法末尾写 return this;。注意不要在构造函数里返回别的东西,也不要意外覆盖 this。
class QueryBuilder {
constructor(data = []) {
this.data = data;
}
where(fn) {
this.data = this.data.filter(fn);
return this; // ← 关键
}
orderBy(key) {
this.data = this.data.sort((a, b) => a[key] - b[key]);
return this; // ← 关键
}
limit(n) {
this.data = this.data.slice(0, n);
return this; // ← 关键
}
get() {
return this.data; // 终止链,返回结果
}
}
// 使用
const result = new QueryBuilder([{id: 2, score: 85}, {id: 1, score: 92}])
.where(item => item.score > 80)
.orderBy('id')
.limit(1)
.get(); // → [{id: 1, score: 92}]
链式调用在真实项目中一碰异步就容易断裂,比如忘记 async/await 或错误地在 Promise 内部返回 this。
then() 回调里 return this,但外层没 await,导致下个方法调用的是 Promise 实例而非原对象this,若在类方法中用箭头函数定义私有工具,再在里面调用 this.xxx(),可能出错end()、exec()、get() 应统一语义,避免有人误写 .get().where(...)
_.chain() 手动开启链式,但现代开发更倾向用原生管道(|> 提案)或纯函数组合,因为链式对象状态难调试链式调用最危险的错觉,是以为“写得短=逻辑清晰”。实际上,超过 4 步的链式(尤其含条件分支)会让调试变得困难——你没法在中间某步打 debugger 看 this.data 是什么,除非每步都拆出来赋值。真要兼顾可读与可维护,不如把长链拆成带语义变量名的步骤,哪怕多写两行。