TypeScript类中如何正确传递this上下文给外部方法?
问题分析与解决方案
问题代码
const actions = { doSomething: (this: B, foo: boolean) => { console.log('from do something', this.text, foo); } } class B { actions = actions; text: string; constructor() { this.text = 'abc'; } } class A extends B { runAction(foo) { this.actions.doSomething(false); } } const instance = new A(); instance.runAction(); // 输出:'from do something, undefined, false'
问题根源:箭头函数的this是定义时的上下文(此处为全局/模块上下文),无法通过调用时的上下文改变,所以调用doSomething时,this指向不是类实例,导致this.text为undefined。
以下是几种适合大量(500+)action场景的解决方案,无需传递类实例参数:
方案1:批量绑定this到类实例
将actions中的箭头函数改为普通函数,在B类的构造函数中批量把每个action的this绑定到当前实例:
const actions = { doSomething: function(this: B, foo: boolean) { console.log('from do something', this.text, foo); }, // 其他500+个action... }; class B { actions: typeof actions; text: string; constructor() { this.text = 'abc'; // 初始化actions并批量绑定this this.actions = {} as typeof actions; for (const key in actions) { if (Object.prototype.hasOwnProperty.call(actions, key)) { this.actions[key as keyof typeof actions] = actions[key].bind(this); } } } } class A extends B { runAction(foo: boolean) { this.actions.doSomething(false); } } const instance = new A(); instance.runAction(true); // 输出:from do something abc false
优点:改动小,原有调用方式不变;缺点:每个实例会复制一份绑定后的函数,内存占用略高。
方案2:使用混入(Mixin)将actions转为类方法
把actions作为混入挂载到类的原型上,让每个action直接成为类的方法,自然继承实例的this:
// 定义actions集合 const actions = { doSomething(this: B, foo: boolean) { console.log('from do something', this.text, foo); }, // 其他500+个action... }; // 混入函数:将actions挂载到类原型 function withActions<T extends new (...args: any[]) => {}>(Base: T) { class WithActions extends Base {} Object.assign(WithActions.prototype, actions); return WithActions as T & typeof actions; } // 基础B类 class BBase { text: string; constructor() { this.text = 'abc'; } } // 应用混入得到最终B类 const B = withActions(BBase); type B = InstanceType<typeof B>; class A extends B { runAction(foo: boolean) { this.doSomething(false); // 直接调用,无需通过actions属性 } } const instance = new A(); instance.runAction(true); // 输出:from do something abc false
优点:所有实例共享原型上的方法,内存高效;调用更简洁;缺点:需要调整调用方式(去掉.actions)。
方案3:用Proxy自动绑定this
通过Proxy代理actions对象,当访问方法时自动绑定当前实例的this,完全不改动原有actions结构:
const actions = { doSomething(this: B, foo: boolean) { console.log('from do something', this.text, foo); }, // 其他500+个action... }; class B { actions: typeof actions; text: string; constructor() { this.text = 'abc'; // 用Proxy自动绑定this this.actions = new Proxy(actions, { get: (target, prop: keyof typeof actions) => { const method = target[prop]; return typeof method === 'function' ? method.bind(this) : method; }, }); } } class A extends B { runAction(foo: boolean) { this.actions.doSomething(false); } } const instance = new A(); instance.runAction(true); // 输出:from do something abc false
优点:零改动原有actions,调用方式完全不变;缺点:需要理解Proxy的工作原理,低版本环境可能不支持(但现代浏览器/Node.js已完全支持)。
内容的提问来源于stack exchange,提问作者Shannon Hochkins
相关产品推荐
相关产品推荐

