JavaScript命名空间对象:this无法使用?异步调用如何返回值?
var CustomUtils = { _constPageID: 12345, // This is called with the results from from FB.getLoginStatus(). _statusChangeCallback: function(response) { console.log('statusChangeCallback'); console.log(response); var callback = function(data) { alert('callback: ' + data); //actually I want the data here to be the return value of my main function below (at the end) }; if (response.status === 'connected') { // Logged into your app and Facebook. CustomUtils._getToken(callback); } }, /* main function to get a token */ getAndReturnToken: function() { $.ajaxSetup({ cache: true }); $.getScript('https://connect.facebook.net/en_US/sdk.js', function(){ FB.init({ appId: '....appid......', autoLogAppEvents : true, xfbml : true, version: 'v3.0' }); FB.getLoginStatus(CustomUtils._statusChangeCallback); }); }, };
1. 为什么替换成this会报错,_constPageID却能用?
这核心是JavaScript中this的指向规则在搞鬼。当你把CustomUtils._statusChangeCallback换成this._statusChangeCallback时,你只是把这个函数的引用传给了FB.getLoginStatus,但等Facebook SDK调用这个回调时,函数内部的this已经不再指向CustomUtils了——它的上下文变成了SDK内部的执行环境(非严格模式下甚至会变成全局window对象)。这时候this._statusChangeCallback自然找不到对应的方法,所以报错。
那为什么this._constPageID能正常用?因为你使用它的场景是在CustomUtils的方法内部直接访问,比如在getAndReturnToken里调用时,this确实指向CustomUtils(方法调用时,this默认指向调用该方法的对象)。
解决这个问题的常用方法有三种:
- 用
bind绑定this:手动把回调的this固定为CustomUtils,比如:FB.getLoginStatus(this._statusChangeCallback.bind(this)); - 使用箭头函数:箭头函数会继承外层作用域的
this,把_statusChangeCallback改成箭头函数后,内部的this会一直指向CustomUtils:_statusChangeCallback: (response) => { console.log('statusChangeCallback'); console.log(response); // 这里的this始终指向CustomUtils // ...其他代码 } - 提前保存
this到变量:在getAndReturnToken里先把this存成self,后续回调里用这个变量代替this:getAndReturnToken: function() { const self = this; $.ajaxSetup({ cache: true }); $.getScript('https://connect.facebook.net/en_US/sdk.js', function(){ FB.init({/*...*/}); FB.getLoginStatus(self._statusChangeCallback); }); },
2. 异步函数如何返回token供其他代码使用?
异步操作的本质就是无法同步返回结果——getAndReturnToken会在AJAX和Facebook SDK的异步操作完成前就执行完毕,直接返回的话只能得到undefined。目前主流的解决方案有以下几种:
方案一:使用Promise(最推荐)
把getAndReturnToken改成返回一个Promise,当token获取成功时调用resolve传递token,失败时调用reject传递错误信息。其他代码可以用.then()或者async/await来获取token,写法更优雅:
修改后的核心代码示例:
var CustomUtils = { _constPageID: 12345, _statusChangeCallback: function(response, resolve, reject) { if (response.status === 'connected') { // 把_getToken也改成返回Promise this._getToken().then(token => { resolve(token); }).catch(err => { reject(err); }); } else { reject(new Error('用户未登录Facebook')); } }, _getToken: function() { return new Promise((resolve, reject) => { // 这里写获取token的异步逻辑,成功时resolve(token),失败时reject(err) }); }, getAndReturnToken: function() { return new Promise((resolve, reject) => { $.ajaxSetup({ cache: true }); $.getScript('https://connect.facebook.net/en_US/sdk.js', () => { FB.init({ appId: '....appid......', autoLogAppEvents : true, xfbml : true, version: 'v3.0' }); FB.getLoginStatus((response) => { this._statusChangeCallback(response, resolve, reject); }.bind(this)); }); }); }, };
使用方式:
// 用then/catch链式调用 CustomUtils.getAndReturnToken().then(token => { // 拿到token后执行后续API调用 console.log('获取到的token:', token); }).catch(err => { console.error('获取token失败:', err); }); // 或者在async函数里用await(更像同步写法) async function fetchDataWithToken() { try { const token = await CustomUtils.getAndReturnToken(); console.log('获取到的token:', token); // 后续依赖token的操作 } catch (err) { console.error('出错了:', err); } }
方案二:使用回调函数(传统方式)
虽然你不想在回调里写后续逻辑,但这是早期JS处理异步的标准方式。可以把后续逻辑封装成函数,作为参数传给getAndReturnToken,等token获取到后再调用这个函数:
getAndReturnToken: function(onSuccess, onError) { $.ajaxSetup({ cache: true }); $.getScript('https://connect.facebook.net/en_US/sdk.js', () => { FB.init({/*...*/}); FB.getLoginStatus((response) => { if (response.status === 'connected') { this._getToken((token) => { onSuccess(token); }, onError); } else { onError(new Error('用户未登录')); } }.bind(this)); }); },
使用方式:
CustomUtils.getAndReturnToken((token) => { // 拿到token执行后续操作 }, (err) => { // 处理错误 });
内容的提问来源于stack exchange,提问作者rishijd

