angularjs http拦截器类(ES6)失去对'this'的绑定

Ken*_*son 16 interceptor angularjs ecmascript-6 traceur gulp-traceur

我正在使用ES6类构建AngularJS应用程序,并将跟踪器转换为AMD格式的ES5.

在我的模块中,我导入拦截器类并将其注册为服务,然后使用module.config中的$ httpProvider.interceptors注册此服务:

var commonModule = angular.module(moduleName, [constants.name]);

import authenticationInterceptor from './authentication/authentication.interceptor';

commonModule.service('authenticationInterceptor', authenticationInterceptor);

commonModule.config( $httpProvider =>  {
    $httpProvider.interceptors.push('authenticationInterceptor');
});
Run Code Online (Sandbox Code Playgroud)

我的拦截器类注入$ q$ window服务,将它们保存在构造函数中供以后使用.我使用调试器跟踪了这一部分并正确地进行了注入:

'use strict';
/*jshint esnext: true */

var authenticationInterceptor = class AuthenticationInterceptor {

    /* ngInject */
    constructor($q, $window) {
        this.$q = $q;
        this.$window = $window;
    }

    responseError(rejection) {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}

authenticationInterceptor.$inject = ['$q', '$window'];

export default authenticationInterceptor;
Run Code Online (Sandbox Code Playgroud)

当我发出一个响应401的请求时,拦截器会正确触发,但是在'responseError'方法中,'this'变量指向窗口对象而不是我的拦截器,因此我无法访问它.$ q这个.$ window.

我想不通为什么?有任何想法吗?

Mer*_*ott 27

context(this)丢失是因为Angular框架只保留对处理函数本身的引用,并且在没有任何上下文的情况下直接调用它们,正如alexpods指出的那样.

我最近写了一篇关于$http使用TypeScript 编写拦截器的博客文章,该文章也适用于ES6类:AngularJS 1.x拦截器使用TypeScript.

总结一下我在本文中讨论过的内容,为了不丢失this处理程序,你必须将方法定义为箭头函数,有效地将函数直接放在constructor编译后的ES5代码中的类函数内.

class AuthenticationInterceptor {

    /* ngInject */
    constructor($q, $window) {
        this.$q = $q;
        this.$window = $window;
    }

    responseError = (rejection) => {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你真的坚持上有你的拦截器写成一个完全基于原型的类,你可以定义一个基类为您拦截并扩展它.基类会用实例方法替换原型拦截器函数,所以我们可以编写这样的拦截器:

class HttpInterceptor {
  constructor() {
    ['request', 'requestError', 'response', 'responseError']
        .forEach((method) => {
          if(this[method]) {
            this[method] = this[method].bind(this);
          }
        });
  }
}

class AuthenticationInterceptor extends HttpInterceptor {

    /* ngInject */
    constructor($q, $window) {
        super();
        this.$q = $q;
        this.$window = $window;
    }

    responseError(rejection) {
        var authToken = rejection.config.headers.Authorization;
        if (rejection.status === 401 && !authToken) {
            let authentication_url = rejection.data.errors[0].data.authenticationUrl;
            this.$window.location.replace(authentication_url);
            return this.$q.defer(rejection);
        }
        return this.$q.reject(rejections);
    }
}
Run Code Online (Sandbox Code Playgroud)


ale*_*ods 4

看看这些源代码行

// apply interceptors
forEach(reversedInterceptors, function(interceptor) {
    if (interceptor.request || interceptor.requestError) {
        chain.unshift(interceptor.request, interceptor.requestError);
    }
    if (interceptor.response || interceptor.responseError) {
        chain.push(interceptor.response, interceptor.responseError);
    }
});
Run Code Online (Sandbox Code Playgroud)

interceptor.responseError方法被推入链时,它会失去它的上下文(只是函数被推入,没有任何上下文);

稍后它将作为拒绝回调添加到 Promise 中

while (chain.length) {
    var thenFn = chain.shift();
    var rejectFn = chain.shift();

    promise = promise.then(thenFn, rejectFn);
}
Run Code Online (Sandbox Code Playgroud)

因此,如果 Promise 被拒绝,rejectFn(你的responseError函数)将作为普通函数执行。在这种情况下this,引用window脚本是否正在非严格模式下执行,否则等于null

恕我直言,Angular 1 是考虑到 ES5 编写的,所以我认为将它与 ES6 一起使用并不是一个好主意。