如何将范围保持在 XHR 请求中?

How to keep scope within an XHR request?

所以我正在构建一个 Javascript 路由器并构建这样的路由:

route('/user/{name}', 'page', function() {
    this.text = 'User: ' + this.name;
});

函数的作用域是当前路由,所以我可以在这里操作当前路由(this.text是view找的)

现在我的下一步是在路由中包含一个 XHR 请求,它类似于:

route('/user/{name}', 'page', function() {
    this.text = 'Loading';

    var request = new XMLHttpRequest();
    request.open('GET', '/api/user', true);

    request.onreadystatechange = (function() {
        if (this.readyState === 4) {
            if (this.status >= 200 && this.status < 400) {
                var data = JSON.parse(this.responseText);
                // here is the issue: 'this' now refers to
                // the XHR request and not the scope of the router
                this.age = data.age;
                this.gender = data.gender;
            } else {
                this.text = "Error";
            }
        }
    })/* .bind(this); */ // keeping scope like this overwrites the XHR

    request.send();
    request = null;
});

这里的问题是我需要访问 XHR 范围 我的路由器范围。在 onreadystatechange 末尾使用 .bind 会覆盖 XHR 范围,而不设置它会覆盖路由器的范围。

那怎么办?有没有比 var that = this; 更干净的东西——肯定有办法吗?

怎么样:

route('/user/{name}', 'page', function() {
    var routeScope = this;

    request.onreadystatechange = (function() {
        if (this.readyState === 4) {
            if (this.status >= 200 && this.status < 400) {
                console.log(this);
                // "this" is xhr's scope, while 
                console.log(routeScope);
                // routeScope is your router's scope
                // ...
            }
        }
    });
})

编辑:错误,只读了你的最后一句话......没关系。

最简单(也是非常清晰的方法)是像这样保持对路由范围的引用:

var that = this;

您还可以使用 .bind() 设置范围并直接从 reqest 变量访问请求属性。

对于您的示例(使用 bind 辅助函数,以支持旧浏览器):

var bind = function(fn, context) {
    return function() {
        fn.apply(context, arguments);
    };
};

request.onreadystatechange = bind(function() {
  if (request.readyState === 4) {
    if (request.status >= 200 && request.status < 400) {
      var data = JSON.parse(request.responseText);
      this.age = data.age;
      this.gender = data.gender;
    } else {
      this.text = "Error";
    }
  }
}, this);