派生类构造函数中Proxy方法里的`this`为何为undefined?
继承类中使用Proxy代理方法时
this为undefined的问题及解决方案 场景描述
尝试通过继承类并为方法定义Proxy对象,来覆盖/代理一个库类的函数。但派生类中的this对象为undefined,且现有解决方案因Proxy的使用无法生效。
示例代码
class Structure { constructor (name){ this.noDoors = 0 this.name = name } async addDoors(noDoorsToAdd){ this.noDoors += noDoorsToAdd console.log(`${noDoorsToAdd} doors added to ${this.name}`) return new Promise(r=>r()) } } const Building = class Building extends Structure { constructor(conditionfunc,args){ super(args) this.addDoors = new Proxy(this.addDoors, { apply(target, thisArg, argumentsList){ //console.log("apply:",target, thisArg, argumentsList,conditionfunc(argumentsList[0])) if (conditionfunc(argumentsList[0])){ console.log(`Correct password: About to add doors to ${thisArg.name}`) target(argumentsList[1]); } else { console.log(`Incorrect password: Doors not added to ${thisArg.name}`) } } }) } } /******** Main Script *********/ let conditionfunc = (password) => { if (password == '123456') { return true } else {return false } } myStructure = new Structure('Ty Bach') myBuilding = new Building(conditionfunc,'Ty Mawr') ;(async()=>{ await myStructure.addDoors(1) await myBuilding.addDoors('wrongpassword',7) await myBuilding.addDoors('123456',4) console.log(`${myStructure.name} has ${myStructure.noDoors} doors!`) console.log(`${myBuilding.name} has ${myBuilding.noDoors} doors!`) })();
预期与实际结果
预期myBuilding对象最终会有4个门,但控制台报错如下:
1 doors added to Ty Bach Doors not added to Ty Mawr About to add doors to Ty Mawr Ty Bach has 1 doors! Ty Mawr has 0 doors! (node:3732) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'noDoors' of undefined at addDoors (MYPATH\proxytoy\test.js:11:3) at Object.apply (MYPATH\proxytoy\test.js:26:6) at MYPATH\proxytoy\test.js:51:23 at processTicksAndRejections (internal/process/task_queues.js:95:5) (Use `node --trace-warnings ...` to show where the warning was created) (node:3732) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) (node:3732) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
问题
- 派生类对象中
this为何为undefined? - 如何修复该问题,或通过其他方式实现相同需求?
解答
问题1:this为undefined的原因
直接调用target(argumentsList[1])时,没有给原方法addDoors绑定正确的上下文。类的实例方法的this依赖于调用时的上下文,这里直接调用target(原addDoors函数)属于独立调用状态,而类内部方法默认运行在严格模式中,此时this会被设为undefined,因此触发报错。另外,原方法是async函数,调用时未返回Promise,也导致了未处理的Promise拒绝警告。
问题2:修复方案及替代实现
方案1:修复Proxy中的方法调用
在Proxy的apply陷阱里,使用call或apply给原方法绑定正确的上下文thisArg,同时正确传递参数并返回Promise:
const Building = class Building extends Structure { constructor(conditionfunc, args) { super(args) this.addDoors = new Proxy(this.addDoors, { async apply(target, thisArg, argumentsList) { if (conditionfunc(argumentsList[0])) { console.log(`Correct password: About to add doors to ${thisArg.name}`) // 绑定上下文并传递参数,返回原方法的Promise return target.call(thisArg, argumentsList[1]); } else { console.log(`Incorrect password: Doors not added to ${thisArg.name}`) // 返回空Promise避免未处理拒绝 return Promise.resolve(); } } }) } }
方案2:不使用Proxy,直接重写方法
若不需要Proxy的额外特性,直接在派生类中重写addDoors方法更直观:
const Building = class Building extends Structure { constructor(conditionfunc, args) { super(args) this.conditionfunc = conditionfunc; } async addDoors(password, noDoorsToAdd) { if (this.conditionfunc(password)) { console.log(`Correct password: About to add doors to ${this.name}`) // 调用父类方法并绑定当前实例上下文 return super.addDoors(noDoorsToAdd); } else { console.log(`Incorrect password: Doors not added to ${this.name}`) return Promise.resolve(); } } }
两种方案都能解决this的问题,最终myBuilding的noDoors会正确更新为4。
内容的提问来源于stack exchange,提问作者Lee
相关产品推荐
相关产品推荐

