2012-07-09 18 views
7

Tôi gặp vấn đề vẫn làm phiền tôi trên js oop - Tôi chắc chắn tôi đang làm điều đó xấu, nhưng tôi không thể làm thế nào để làm điều đó đúng.Javascript OOP - mất điều này trong gọi lại không đồng bộ

Ví dụ, tôi có mã này

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

Vấn đề là tôi không thể truy cập vào chức năng setToken từ bối cảnh "request.onloadend" chức năng - nó có lẽ vì tôi đã mất liên quan đến "này".

Giải pháp cho vấn đề này là gì? Tôi có thể bằng cách nào đó vượt qua "này" var để bối cảnh của chức năng này?

Cảm ơn!

Trả lời

4

Có một vài cách để làm điều này. Các trực tiếp nhất là chỉ cần lưu một bản sao của các giá trị mà bạn cần:

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

Một cách khác là sử dụng 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); 

Cách tiếp cận thứ hai là "sạch", nhưng nó có vấn đề về khả năng tương thích trình duyệt (IE < 9 không hỗ trợ nó).

+0

Tôi nghĩ người kia là trực tiếp hơn, mã được thay đổi tối thiểu và bạn không cần phải quyết định từ khóa giả để sử dụng. Chỉ cần nói. – Esailija

+0

@Esailija: Tôi cũng vậy, nhưng thật đáng buồn là giá trị thực tế của nó bị hạn chế bởi tính tương thích của trình duyệt. – Jon

+0

Điểm tranh luận với 'XHR.onloaded' – Esailija

1

Bạn chỉ có thể nắm bắt được một tham chiếu đến nó trong phạm vi bên ngoài, tôi đã sử dụng các định danh self, tuy nhiên xin vui lòng cho biết tên các ý nghĩa ngữ nghĩa hơn:

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

Capture this trước khi gọi lại:

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 chức năng:

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

Lưu this trong một var địa phương bên ngoài gọi lại.

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

Bạn đang hoàn toàn đúng: callback được gọi với đối tượng XMLHTTPRequest như bối cảnh (ví dụ: giá trị của this). Bạn cần phải cung cấp cho trường hợp của bạn một tên khác, vì vậy mà bạn có thể truy cập vào nó trong phạm vi của callback:

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. Tôi đã sử dụng authInstance thay vì self, bởi vì tôi nghĩ rằng thường tốt khi sử dụng tên biến mô tả; bạn sẽ không bao giờ phải tìm ra những gì authInstance có nghĩa là, trong khi self có thể không rõ ràng khi ai đó trong tương lai (có thể bạn!) đọc mã.

Một tùy chọn khác là sử dụng bind, nhưng điều đó có lẽ phức tạp hơn mức cần thiết tại đây.

+0

Sửa đổi hầu hết mã và sử dụng 2 từ khóa" riêng biệt "cũng như biến phụ ít phức tạp hơn là chỉ đơn giản là ném' .bind (this) 'tại kết thúc? – Esailija

+0

@Esailija Tôi nghĩ rằng hình thức 'bind' ít trực quan và dễ đọc hơn. Ngữ cảnh chỉ trở nên rõ ràng ở phần cuối của hàm, và hàm này khá dài. Nếu đó là chức năng hai dòng, tôi đồng ý với bạn. – lonesomeday

+0

Bên trong một lớp 'this' đề cập đến trường hợp hiện tại của lớp ([không có vấn đề làm thế nào ràng buộc được thực hiện] (http://en.wikipedia.org/wiki/Dynamic_dispatch)), là trực quan hơn trong quan điểm của tôi. Nhưng chúng ta có thể không đồng ý về điều đó. – Esailija

Các vấn đề liên quan