'this' không được xác định trong các phương thức lớp JavaScript


83

Tôi mới làm quen với JavaScript. Mới như tất cả những gì tôi thực sự đã làm với nó là chỉnh sửa mã hiện có và viết các bit nhỏ của jQuery.

Bây giờ tôi đang cố gắng viết một "lớp" với các thuộc tính và phương thức, nhưng tôi đang gặp sự cố với các phương thức. Mã của tôi:

function Request(destination, stay_open) {
    this.state = "ready";
    this.xhr = null;
    this.destination = destination;
    this.stay_open = stay_open;

    this.open = function(data) {
        this.xhr = $.ajax({
            url: destination,
            success: this.handle_response,
            error: this.handle_failure,
            timeout: 100000000,
            data: data,
            dataType: 'json',
        });
    };

    /* snip... */

}

Request.prototype.start = function() {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
//all console.log's omitted

Vấn đề là, trong Request.prototype.start, thiskhông được xác định và do đó câu lệnh if đánh giá là false. Tôi làm gì sai ở đây?


Có một lý do bạn có starttrong prototype?
xj9

Cái gì Request.prototypeđược đặt thành?
Matt Ball

Tôi đã có một câu hỏi tương tự ở đây: stackoverflow.com/questions/3198264/… trong đó có một loạt các liên kết hữu ích. Điểm mấu chốt của nó là thistrong JavaScript không phải là một tham chiếu liên tục đến 'chủ sở hữu' của một hàm nguyên mẫu đang được gọi, giống như trong hầu hết các ngôn ngữ OO như Java.
Marc Bollinger

1
@Matt: Yêu cầu là một hàm khởi tạo. Request.prototype được mặc định thành new Object(). Bất cứ thứ gì bạn thêm vào nó sẽ tự động trở thành thuộc tính của các đối tượng được tạo bằng cách sử dụng new Request().
Chetan S

@Matt Ball Request.prototypelà nơi chứa các trường hợp Requestkế thừa từ. Trong trường hợp này nó có thể là Functionhoặc Object.
xj9

Câu trả lời:


67

Bạn đang gọi hàm start như thế nào?

Điều này sẽ hoạt động ( mới là chìa khóa)

var o = new Request(destination, stay_open);
o.start();

Nếu bạn trực tiếp gọi nó như thế Request.prototype.start(), thissẽ đề cập đến ngữ cảnh toàn cầu ( windowtrong các trình duyệt).

Ngoài ra, nếu thiskhông được xác định, nó dẫn đến lỗi. Biểu thức if không đánh giá thành false.

Cập nhật : thisđối tượng không được thiết lập dựa trên khai báo, mà bằng cách gọi . Điều đó có nghĩa là nếu bạn gán thuộc tính hàm cho một biến like x = o.startvà call x(), thì thisbên trong start không còn tham chiếu đến onữa. Đây là những gì xảy ra khi bạn làm setTimeout. Để làm cho nó hoạt động, hãy làm điều này thay thế:

 var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);

Tôi đang sử dụng setTimeout:var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
Carson Myers

Điều này đã cứu mạng tôi, khi cố gắng hiểu tại sao hàm tôi đang chuyển để biểu thị ' basicAuth không hoạt động với cùng một đầu ra.
Edison Spencer

Hoặc làm o.start.bind(o). Tại sao không x = o.start; x()hoạt động?
theonlygusti

35

Tôi chỉ muốn chỉ ra rằng đôi khi lỗi này xảy ra vì một hàm đã được sử dụng như một hàm bậc cao (được truyền dưới dạng đối số) và sau đó phạm vi thisbị mất. Trong những trường hợp như vậy, tôi khuyên bạn nên chuyển hàm như vậy bị ràng buộc đến this. Ví dụ

this.myFunction.bind(this);

3
Lời giải thích hay !! Đó chính xác là những gì tôi đang tìm kiếm.
vandersondf

1
Không có gì để nói rằng bạn vừa cứu tôi đau đầu đến mức nào
Native Coder

Tại sao phạm vi thisbị mất?
theonlygusti

17

OOP của JavaScript hơi thú vị (hoặc nhiều) và phải mất một số thời gian để làm quen. Điều đầu tiên bạn cần lưu ý là không có Lớp học và việc suy nghĩ về lớp học có thể khiến bạn thăng tiến. Và để sử dụng một phương thức được gắn với một Constructor (JavaScript tương đương với định nghĩa Lớp), bạn cần phải khởi tạo đối tượng của mình. Ví dụ:

Ninja = function (name) {
    this.name = name;
};
aNinja = new Ninja('foxy');
aNinja.name; //-> 'foxy'

enemyNinja = new Ninja('boggis');
enemyNinja.name; //=> 'boggis'

Lưu ý rằng các Ninjacá thể có cùng thuộc tính nhưng aNinjakhông thể truy cập thuộc tính của enemyNinja. (Phần này phải thực sự dễ dàng / đơn giản) Mọi thứ sẽ khác một chút khi bạn bắt đầu thêm nội dung vào prototype:

Ninja.prototype.jump = function () {
   return this.name + ' jumped!';
};
Ninja.prototype.jump(); //-> Error.
aNinja.jump(); //-> 'foxy jumped!'
enemyNinja.jump(); //-> 'boggis jumped!'

Gọi điều này trực tiếp sẽ gây ra lỗi vì thischỉ trỏ đến đúng đối tượng ("Lớp" của bạn) khi Khối mã lệnh được khởi tạo (nếu không, nó trỏ đến đối tượng toàn cục, windowtrong trình duyệt)


6

Trong ES2015 hay còn gọi là ES6, classlà một đường cú pháp cho functions.

Nếu bạn muốn buộc thiết lập một ngữ cảnh cho thisbạn, bạn có thể sử dụng bind()phương thức. Như @chetan đã chỉ ra, khi gọi bạn cũng có thể đặt bối cảnh! Kiểm tra ví dụ dưới đây:

class Form extends React.Component {
constructor() {
    super();
  }
  handleChange(e) {
    switch (e.target.id) {
      case 'owner':
        this.setState({owner: e.target.value});
        break;
      default:
    }
  }
  render() {
    return (
      <form onSubmit={this.handleNewCodeBlock}>
        <p>Owner:</p> <input onChange={this.handleChange.bind(this)} />
      </form>
    );
  }
}

Ở đây chúng ta buộc bối cảnh bên trong handleChange()để Form.


6
Bạn nên liên kết hàm này với hàm này trong hàm tạo. Nếu không, bạn đang ràng buộc nó mỗi lần renderđược gọi thay vì một lần khi lớp được khởi tạo. Ngoài ra, hầu hết ví dụ của bạn không thực sự liên quan đến câu hỏi.
erich2k8

Hoặc sử dụng cú pháp mũi tên khi xác địnhhandleChange()
Nitin

0

Câu hỏi này đã được trả lời, nhưng có thể điều này có thể có người khác đến đây.

Tôi cũng đã gặp sự cố thiskhông được xác định, khi tôi đã cố gắng phá hủy các phương thức của một lớp khi khởi tạo nó một cách ngu ngốc:

import MyClass from "./myClass"

// 'this' is not defined here:
const { aMethod } = new MyClass()
aMethod() // error: 'this' is not defined

// So instead, init as you would normally:
const myClass = new MyClass()
myClass.aMethod() // OK

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.