2012-07-09 33 views
7

我有問題,仍然困擾着我對js oop - 我確信我做得不好,但我不能得到如何做到這一點。Javascript 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」var傳遞給此函數的上下文嗎?

謝謝!

回答

4

有一對夫婦的如何做到這一點。最直接的就是簡單地保存值的副本,你需要:

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

+0

我認爲另一個更直接,代碼是最小改變,你不需要決定使用哪個僞關鍵字。只是說。 – Esailija 2012-07-09 10:48:12

+0

@Esailija:我也是,但不幸的是它的實用價值受到瀏覽器兼容性的限制。 – Jon 2012-07-09 10:49:41

+0

模擬點與'XHR.onloaded' – Esailija 2012-07-09 10:50:51

1

你可以只捕捉到外部範圍對它的引用,我已經使用了標識self,但是請隨時給予名稱更語義:

var self = this; 
request.onloadend = function() { 
    ... 
    self.setToken(token); 
    ... 
}; 
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.'); 
     } 
    } 
} 
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 
} 
0

保存this回調外的本地變量。

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.'); 
     } 
    } 
} 
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,但這可能比這裏所需的更復雜。

+0

修改大部分代碼,並使用2個單獨的「關鍵字」以及一個額外的變量比簡單地拋出'.bind(this)'更簡單結束? – Esailija 2012-07-09 11:00:13

+0

@Esailija我認爲'bind'表單不夠直觀和清晰。在函數結束時,上下文才變得清晰,並且該函數非常長。如果這是一個雙線功能,我同意你的看法。 – lonesomeday 2012-07-09 11:02:38

+0

在我看來,在指示類的當前實例的類'this'內部([不​​管如何實現綁定](http://en.wikipedia.org/wiki/Dynamic_dispatch))更直觀。但我們可以不同意這一點。 – Esailija 2012-07-09 11:09:09