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

时间:2012-07-09 10:41:56

标签: javascript oop callback this prototype

我有问题仍然困扰我js oop - 我确定我做得不好,但我不能得到如何正确。

例如,我有这段代码

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传递给此函数的上下文吗?

谢谢!

6 个答案:

答案 0 :(得分:5)

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

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不支持它)。

答案 1 :(得分:2)

.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
}

答案 2 :(得分:1)

你可以在外部范围内捕获对它的引用,我使用了标识符self,但是请随意给这个名称更多的语义含义:

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

答案 3 :(得分:1)

在回调之前捕获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.');
      }
    }
}

答案 4 :(得分:0)

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.');
      }
    }
}

答案 5 :(得分:0)

你是对的:使用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.');
      }
    }
}

See this answer to another question for more explanation of why this is necessary。我使用authInstance而不是self,因为我认为使用描述性变量名称通常很好;你永远不会知道authInstance意味着什么,而self可能是未来某人(可能是你!)读取代码时的模糊不清。

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