2016-01-21 45 views
51

Tôi có lớp javascript và mỗi phương thức trả về lời hứa Q. Tôi muốn biết lý do tại sao this không được xác định trong method2method3. Có cách nào chính xác hơn để viết mã này không?Tại sao 'này' không xác định phương thức lớp bên trong khi sử dụng lời hứa?

function MyClass(opts){ 
    this.options = opts; 

    return this.method1() 
    .then(this.method2) 
    .then(this.method3); 
} 

MyClass.prototype.method1 = function(){ 
    // ...q stuff... 

    console.log(this.options); // logs "opts" object 

    return deferred.promise; 
}; 

MyClass.prototype.method2 = function(method1resolve){ 
    // ...q stuff... 

    console.log(this); // logs undefined 

    return deferred.promise; 
}; 

MyClass.prototype.method3 = function(method2resolve){ 
    // ...q stuff... 

    console.log(this); // logs undefined 

    return deferred.promise; 
}; 

tôi có thể khắc phục điều này bằng cách sử dụng bind:

function MyClass(opts){ 
    this.options = opts; 

    return this.method1() 
    .then(this.method2.bind(this)) 
    .then(this.method3.bind(this)); 
} 

Nhưng không hoàn toàn chắc chắn lý do tại sao bind là cần thiết; bị cắt .then() giết this?

+0

Khi bạn sử dụng bind() nó tạo ra một hàm khác với phạm vi chính xác mà bạn sẽ vượt qua bằng tham số. Mặc dù câu trả lời chỉ là câu hỏi cuối cùng của bạn, hãy xem tài liệu của Mozila: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/bind –

+0

Trong 8 từ, hãy giải thích cách heck là [this] (http://stackoverflow.com/questions/34930771/why-is-this-undefined-inside-class-method-when-using-promises) một bản sao của [that] (http: // stackoverflow.com/questions/591269/settimeout-and-this-in-javascript)? Tôi vừa có câu hỏi tương tự chính xác này, câu trả lời sẽ được trả lời bởi [rằng] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript). Tôi biết [rằng] (http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript) đã có, nhưng tôi đang đến với các điều khoản với lời hứa, ES6 Classes, và 'this'. – toszter

+0

Mặc dù nó liên quan chặt chẽ, nhưng đây không phải là một bản sao của câu hỏi này: http://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript Hoặc là "Tại sao một quả táo rơi ra khỏi cây?" một bản sao của câu hỏi "Tại sao một ngôi nhà của thẻ sụp đổ khi tôi nghiêng bàn của nó được xây dựng trên?"? – lex82

Trả lời

71

this luôn là đối tượng mà phương thức được gọi. Tuy nhiên, khi chuyển phương thức sang then(), bạn không gọi nó! Phương pháp này sẽ được lưu trữ ở đâu đó và được gọi từ đó sau đó. Nếu bạn muốn giữ this, bạn sẽ phải làm điều đó như thế này:

.then(() => this.method2()) 

hoặc nếu bạn phải làm điều đó the-ES6 tiền bằng cách nào, bạn cần phải giữ gìn this trước:

var that = this; 
// ... 
.then(function() { that.method2() }) 
+4

câu trả lời tuyệt vời - hoặc tiền ES6 ".then (this.method2.bind (this))" –

+0

Tôi đã sử dụng '.then (dữ liệu => this.method (dữ liệu)) ' – Albert

0

Chức năng một chiều có được ngữ cảnh của chúng (this) là từ đối tượng mà chúng được gọi ra (đó là lý do tại sao method1 có ngữ cảnh phù hợp - được gọi trên this). Bạn đang chuyển một tham chiếu đến chính hàm đó tới then. Bạn có thể tưởng tượng rằng việc thực hiện then trông giống như sau:

function then(callback) { 

    // assume 'value' is the recently-fulfilled promise value 
    callback(value); 
} 

Trong ví dụ rằng callback là một tham chiếu đến chức năng của bạn. Nó không có bất kỳ ngữ cảnh nào. Như bạn đã lưu ý, bạn có thể giải quyết vấn đề đó bằng cách liên kết hàm này với một ngữ cảnh trước khi bạn chuyển nó tới đó.

15

Trình xử lý lời hứa được gọi trong bối cảnh đối tượng chung (window) theo mặc định. Khi ở chế độ nghiêm ngặt (use strict;), ngữ cảnh là undefined. Đây là những gì đang xảy ra với method2method3.

;(function(){ 
    'use strict' 
    Promise.resolve('foo').then(function(){console.log(this)}); // undefined 
}()); 

;(function(){ 
    Promise.resolve('foo').then(function(){console.log(this)}); // window 
}()); 

Đối method1, bạn đang gọi điện thoại method1 như this.method1(). Cách gọi này gọi nó trong ngữ cảnh của đối tượng this là ví dụ của bạn. Đó là lý do tại sao ngữ cảnh bên trong method1 là trường hợp.

+0

Tôi đã phát điên! cho đến khi tôi nhìn thấy câu trả lời của bạn, cảm ơn rất nhiều! –

2

Về cơ bản, bạn chuyển cho nó tham chiếu hàm không có tham chiếu ngữ cảnh. Cảnh báo this được xác định theo một vài cách:

  1. Ngụ ý. Gọi hàm toàn cầu hoặc hàm không có ràng buộc giả định ngữ cảnh chung. *
  2. Bằng cách tham chiếu trực tiếp. Nếu bạn gọi myObj.f() thì myObj sẽ là ngữ cảnh this. **
  3. Ràng buộc thủ công. Đây là lớp chức năng của bạn như .bind.apply. Những điều này bạn nêu rõ bối cảnh this là gì. Những điều này luôn được ưu tiên hơn hai trước đó.

Trong ví dụ của bạn, bạn đang chuyển một tham chiếu hàm, do đó, gọi nó là hàm toàn cầu hoặc hàm không có ngữ cảnh. Sử dụng .bind giải quyết vấn đề này bằng cách tạo một hàm mới trong đó this được đặt rõ ràng.

* Điều này chỉ đúng ở chế độ không nghiêm ngặt. Ở chế độ nghiêm ngặt, this được đặt thành undefined.

** Giả sử chức năng bạn đang sử dụng chưa bị ràng buộc theo cách thủ công.

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