Javascript OOP-在异步回调中丢失了这一点

Javascript OOP - lost this in asynchronous callback

本文关键字:这一点 回调 OOP- 异步 Javascript      更新时间:2023-09-26

我在js-op上遇到了一个问题,这个问题仍然困扰着我——我确信我做得很糟糕,但我不知道如何做对。

例如,我有这个代码

Auth.prototype.auth = function () {
    var request = new XMLHttpRequest();
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        this.setToken(token);
        this.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }
}

问题是我无法从"request.onloadend"函数的上下文访问函数setToken,这可能是因为我丢失了对"this"的引用。

这个问题的解决办法是什么?我能以某种方式将"this"var传递给这个函数的上下文吗?

谢谢!

有几种方法可以做到这一点。最直接的方法是简单地保存您需要的值的副本:

Auth.prototype.auth = function () {
    var request = new XMLHttpRequest();
    var self = this; // save "this" value
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        self.setToken(token); // use saved "this" value
        self.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }
}

另一种方法是使用bind:

request.onloadend = (function () {
  var response = JSON.parse(request.responseText);
  console.log(response);
  if(response.result == 'found') {
    var token = response.token;
    this.setToken(token); // use saved "this" value
    this.isSigned = true;
  } else {
    console.log('Not logged yet.');
  }
}).bind(this);

第二种方法是"更干净",但它存在浏览器兼容性问题(IE<9不支持它)。

.bind函数:

Auth.prototype.auth = function () {
    var request = new XMLHttpRequest();
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        this.setToken(token);
        this.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }.bind(this); //<-- bound
}

您只需在外部范围中捕获对它的引用,我使用了标识符self,但请随意赋予名称更语义的含义:

var self = this;
request.onloadend = function () {
  ...
  self.setToken(token);
  ...
};

回调前捕获this

Auth.prototype.auth = function () {
    var self = this;
    var request = new XMLHttpRequest();
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        self.setToken(token);
        self.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }
}

this保存在回调之外的本地var中。

Auth.prototype.auth = function () {
    var request = new XMLHttpRequest();
    var _this = this;
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        _this.setToken(token);
        _this.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }
}

您说得很对:回调是以XMLHTTPRequest对象作为上下文(即this的值)调用的。你需要给你的实例另一个名称,这样你就可以在回调的范围内访问它:

Auth.prototype.auth = function () {
    var request = new XMLHttpRequest(),
        authInstance = this;
    request.open('GET', this.getAuthServerURL() + '/token', true);
    request.send();
    request.onloadend = function () {
      var response = JSON.parse(request.responseText);
      console.log(response);
      if(response.result == 'found') {
        var token = response.token;
        authInstance.setToken(token);
        authInstance.isSigned = true;
      } else {
        console.log('Not logged yet.');
      }
    }
}

请参阅另一个问题的答案,了解为什么需要这样做的更多解释。我使用了authInstance而不是self,因为我认为使用描述性变量名通常是好的;你永远不需要弄清楚authInstance的意思,而当将来有人(可能是你!)读到代码时,self可能会含糊不清。

另一种选择是使用bind,但这可能比必要时更复杂。