在函数中返回this是否为创建带方法对象的推荐模式?含ES6方案咨询
关于函数返回
this的链式调用模式:是否推荐、替代方案与ES6实现 嘿,这个问题问得特别好!我来帮你梳理清楚这个链式调用模式的来龙去脉~
先明确:你用的是「链式调用/Builder模式」
你提到的在函数里返回this(或上下文对象)实现连续调用的方式,其实是很经典的链式调用模式(也常归为Builder模式的一种)。核心是让方法调用后返回当前对象,从而可以连续调用同一对象的其他方法,让代码更简洁流畅。
这个模式是否推荐?分场景看!
- ✅ 适合的场景:
- 当你需要构建复杂的配置对象、查询语句(比如你的示例里的数据库查询Stub)、或者连续执行同一上下文的操作时,链式调用能大幅提升代码可读性和简洁度。比如jQuery的
$('div').addClass('red').fadeIn()就是典型场景。 - 封装工具类/SDK时,链式调用能让API更友好,降低用户的使用成本。
- 当你需要构建复杂的配置对象、查询语句(比如你的示例里的数据库查询Stub)、或者连续执行同一上下文的操作时,链式调用能大幅提升代码可读性和简洁度。比如jQuery的
- ❌ 不适合的场景:
- 如果只是简单的独立函数调用,强行链式会增加代码耦合度,反而让逻辑晦涩,调试也更麻烦(链式中某一步出错很难定位)。
- 当方法有明确的业务返回值(比如计算结果)时,返回
this会覆盖原本的返回值,造成语义混淆。
为什么你在代码里很少见到它?
其实这个模式并不少见,只是使用场景相对集中:
- 多数业务代码以处理业务逻辑为主,很少需要连续构建复杂对象/操作,所以用得少;
- 很多库(比如Lodash、Axios的拦截器配置)会用,但你可能没注意到;
- 现代前端更倾向于纯函数、Immutable风格的代码,这种无副作用的写法有时候会替代链式调用。
更优/常用的实现方式(避坑!你示例里有个小问题)
先提一下你示例里的陷阱:你用了箭头函数,而箭头函数没有自己的this,这里的return this其实指向外层上下文(比如全局window或者模块的this),根本不是你想要的stubs对象!这会导致链式调用失败。
推荐的实现方式有三种:
1. 显式返回上下文对象(最稳妥)
不管用箭头函数还是普通函数,直接返回要链式的对象,避免this的绑定问题:
const state = {}; const stubs = {}; stubs.collection = sinon.stub().callsFake((collection) => { state.collection = collection; return stubs; // 直接返回stubs,而非this }); stubs.orderBy = sinon.stub().callsFake((field, direction) => { state.orderBy = { field, direction }; return stubs; }); // 使用:链式调用正常工作 stubs.collection('users').orderBy('name', 'asc');
2. 用类封装(更清晰的面向对象写法)
如果逻辑比较复杂,用ES6类封装状态和方法,返回this是很标准的写法:
class QueryStubBuilder { constructor() { this.state = {}; this.stubs = { collection: sinon.stub().callsFake((collection) => { this.state.collection = collection; return this.stubs; }), orderBy: sinon.stub().callsFake((field, direction) => { this.state.orderBy = { field, direction }; return this.stubs; }) }; } getStubs() { return this.stubs; } } // 使用 const builder = new QueryStubBuilder(); const query = builder.getStubs(); query.collection('users').orderBy('name', 'asc');
3. Immutable风格的链式(无副作用)
如果追求纯函数、无副作用的写法,可以每次调用都返回新对象,不修改原对象:
const createQueryStub = (initialState = {}) => ({ collection: (collection) => createQueryStub({...initialState, collection}), orderBy: (field, direction) => createQueryStub({...initialState, orderBy: {field, direction}}), getState: () => initialState }); // 使用:每次调用返回新对象,原对象不受影响 const query = createQueryStub().collection('users').orderBy('name', 'asc'); console.log(query.getState()); // { collection: 'users', orderBy: { field: 'name', direction: 'asc' } }
ES6的现代实现方案
除了上面的类和闭包,还有两个进阶方案:
1. 用Proxy实现动态链式
如果方法是动态生成的,或者需要拦截所有属性访问,可以用ES6的Proxy实现自动链式:
const createDynamicQuery = () => { const state = {}; return new Proxy({}, { get(target, prop) { if (prop === 'getState') return () => state; // 动态生成方法,调用后返回Proxy本身实现链式 return (...args) => { state[prop] = args; return target; }; } }); }; // 使用:不管调用什么方法都能链式 const query = createDynamicQuery().collection('users').orderBy('name', 'asc').limit(10); console.log(query.getState()); // { collection: ['users'], orderBy: ['name', 'asc'], limit: [10] }
2. 用ES模块+闭包封装
在模块里使用时,可以用闭包维护状态,对外暴露可链式的方法,避免全局变量污染:
// queryStub.js import sinon from 'sinon'; export const createQueryStub = () => { const state = {}; const stubs = { collection: sinon.stub().callsFake((col) => { state.collection = col; return stubs; }), orderBy: sinon.stub().callsFake((field, dir) => { state.orderBy = { field, dir }; return stubs; }) }; return { stubs, getState: () => state }; };
总结
- 链式调用(返回
this/上下文对象)是合法且实用的模式,但要根据场景使用,不要滥用; - 你之前示例里的箭头函数
this是个陷阱,建议直接返回目标对象; - ES6里用类、闭包+箭头函数、Proxy都能实现更现代、更安全的链式调用;
- 业务代码里用得少是因为场景不匹配,库/工具类里其实很常见。
内容的提问来源于stack exchange,提问作者cooldude101
相关产品推荐
相关产品推荐

