Node.js:如何在类方法的异步函数中访问this?
解决异步回调中无法访问类实例
this的问题 这个问题我之前也碰到过,核心原因是异步回调函数会改变this的指向——默认情况下,回调里的this指向的是调用回调的上下文(也就是PersonModel.find内部的执行上下文),而不是你的personRepository实例。你试过bind(this)但没生效,应该是绑定的位置不对,咱们一步步来解决:
方法1:使用箭头函数(最简洁的现代方案)
箭头函数不会绑定自己的this,它会继承外层作用域的this,直接把回调改成箭头函数就能解决问题:
var PersonModel = require('./model') class PersonRepository { constructor() { this._persons = new Set(); } getAll(cb) { let results = new Set(); // 把回调函数改成箭头函数 PersonModel.find({}, 'firstName lastName', (err, people) => { if (err) { console.error(err); return cb(err); // 注意原代码里的next(err)应该是cb(err),因为参数是cb } people.forEach((person) => { results.add(person); }); this._persons.add("this._persons 现在可以访问了!"); cb(this._persons); }); this._persons.add("this._persons is accessible"); console.log(this); } } // 测试 var personRepo = new PersonRepository(); personRepo.getAll((persons) => { for (let item of persons) console.log(item); });
方法2:提前保存this到变量(兼容旧环境)
如果你的运行环境不支持箭头函数,可以在回调外部把当前实例的this存成变量(比如self或that),然后在回调里用这个变量访问:
getAll(cb) { let results = new Set(); const self = this; // 保存当前实例的this PersonModel.find({}, 'firstName lastName', function (err, people) { if (err) { console.error(err); return cb(err); } people.forEach((person) => { results.add(person); }); self._persons.add("通过self访问到实例属性了"); cb(self._persons); }); // ...其他代码 }
方法3:正确使用bind(修正你之前的用法)
你之前用PersonModel.find.bind(this)是把find方法的this绑定了,但回调函数的this还是没改。应该给回调函数本身绑定this:
getAll(cb) { let results = new Set(); PersonModel.find({}, 'firstName lastName', function (err, people) { if (err) { console.error(err); return cb(err); } people.forEach((person) => { results.add(person); }); this._persons.add("绑定后能访问实例属性了"); cb(this._persons); }.bind(this)); // 给回调函数绑定当前实例的this // ...其他代码 }
方法4:用async/await重构(更优雅的异步写法)
如果你的环境支持ES6+的async/await,可以把回调风格的代码改成Promise风格,彻底避免this指向问题,代码可读性也更高:
class PersonRepository { constructor() { this._persons = new Set(); } // 改成async函数 async getAll() { try { // 把find方法转成Promise(Mongoose的find默认支持exec()返回Promise) const people = await PersonModel.find({}, 'firstName lastName').exec(); let results = new Set(); people.forEach((person) => { results.add(person); }); this._persons.add("async/await下this正常访问"); return this._persons; // 直接返回结果 } catch (err) { console.error(err); throw err; // 抛出错误让调用者处理 } } } // 调用方式 var personRepo = new PersonRepository(); personRepo.getAll().then((persons) => { for (let item of persons) console.log(item); }).catch(err => console.error(err)); // 或者在async函数里用await: // async function test() { // try { // const persons = await personRepo.getAll(); // for (let item of persons) console.log(item); // } catch (err) { // console.error(err); // } // } // test();
内容的提问来源于stack exchange,提问作者b_it_s
相关产品推荐
相关产品推荐

