var tự = cái này?


182

Sử dụng các phương thức cá thể làm cuộc gọi lại cho các trình xử lý sự kiện sẽ thay đổi phạm vi thistừ "Ví dụ của tôi" thành "Bất cứ điều gì chỉ được gọi là cuộc gọi lại" . Vì vậy, mã của tôi trông như thế này

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Nó hoạt động, nhưng đó là cách tốt nhất để làm điều đó? Nó có vẻ lạ đối với tôi.


15
Bạn nên tránh selfvì có một window.selfđối tượng và cuối cùng bạn có thể vô tình sử dụng nó nếu bạn quên khai báo selfvar của chính mình (ví dụ như khi di chuyển một số mã xung quanh). Điều này có thể gây khó chịu để phát hiện / gỡ lỗi. Tốt hơn để sử dụng một cái gì đó như _this.
Alastair Maw


3
Từ Hướng dẫn JavaScript : "Giá trị của thisđộng trong JavaScript. Nó được xác định khi hàm được gọi , không phải khi nó được khai báo."
DavidRR


Điều này là, trong phạm vi toàn cầu self === this. Do đó, selftrong bối cảnh địa phương có ý nghĩa và đang theo mô hình.
lập trình

Câu trả lời:


210

Câu hỏi này không dành riêng cho jQuery, nhưng cụ thể đối với JavaScript nói chung. Vấn đề cốt lõi là làm thế nào để "kênh" một biến trong các hàm nhúng. Đây là ví dụ:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Kỹ thuật này dựa vào việc sử dụng một đóng cửa. Nhưng nó không hoạt động với thisthismột biến giả có thể thay đổi từ phạm vi sang phạm vi một cách linh hoạt:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

Chúng ta có thể làm gì? Gán nó cho một số biến và sử dụng nó thông qua bí danh:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thiskhông phải là duy nhất về mặt này: argumentslà biến giả khác cần được xử lý theo cùng một cách - bằng cách đặt bí danh.


7
Bạn thực sự nên liên kết "cái này" với lệnh gọi hàm và không chuyển nó xung quanh hoặc vượt qua phạm vi biến với nó
michael

10
@michael Tại sao vậy?
Chris Anderson

@michael bạn có thể đang làm việc bên trong đường ống, bản đồ, đăng ký ... trong một chức năng và không gọi các chức năng, trong trường hợp này "điều này" đang thay đổi, vì vậy phạm vi biến là giải pháp
Skander Jenhani

@SkanderJenhani nhận xét đó là 8 tuổi. Câu trả lời của tôi sẽ rất khác ngày hôm nay
michael

18

Vâng, điều này dường như là một tiêu chuẩn chung. Một số lập trình viên sử dụng bản thân, những người khác sử dụng tôi. Nó được sử dụng như một tài liệu tham khảo trở lại đối tượng "thực" trái ngược với sự kiện.

Đó là một cái gì đó khiến tôi mất một chút thời gian để thực sự có được, nó trông có vẻ kỳ lạ lúc đầu.

Tôi thường làm điều này ngay trên đầu đối tượng của mình (xin lỗi mã demo của tôi - nó mang tính khái niệm hơn bất kỳ thứ gì khác và không phải là một bài học về kỹ thuật mã hóa xuất sắc):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}

12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

chỉnh sửa: mặc dù, các hàm lồng nhau trong một đối tượng sẽ lấy đối tượng cửa sổ toàn cục thay vì đối tượng xung quanh.


Bạn dường như đang mô tả những gì var self = thislàm, nhưng đó không phải là những gì câu hỏi đang hỏi (câu hỏi đang hỏi nếu đó là giải pháp tốt nhất cho vấn đề).
Quentin

3
"Vấn đề cốt lõi là làm thế nào để" kênh "một biến trong các hàm nhúng." Tôi đồng ý tuyên bố này trong câu trả lời được chấp nhận. Bên cạnh đó, không có gì sai với thực hành "đó". Nó là khá phổ biến. Vì vậy, như một câu trả lời cho "mã có vẻ lạ đối với tôi" phần tôi đã chọn để giải thích cách nó hoạt động. Tôi tin rằng lý do "mã trông lạ đối với tôi" xuất phát từ sự nhầm lẫn về cách thức hoạt động của nó.
serkan

12

Nếu bạn đang thực hiện ES2015 hoặc thực hiện tập lệnh loại và ES5 thì bạn có thể sử dụng các hàm mũi tên trong mã của mình và bạn không gặp phải lỗi đó và điều này đề cập đến phạm vi mong muốn của bạn trong ví dụ của bạn.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});

5
Như một lời giải thích: Trong ES2015, các hàm mũi tên chụp thistừ phạm vi xác định của chúng. Định nghĩa hàm bình thường không làm điều đó.
nói xấu

@defnull Tôi không nghĩ nó có gì đặc biệt. Các hàm tạo một phạm vi khối. Vì vậy, việc sử dụng ký hiệu mũi tên thay vì chức năng giúp bạn không tạo phạm vi, vì vậy, điều này sẽ vẫn được đề cập đến bên ngoài biến này.
Nimeshka Srimal

4

Một giải pháp cho vấn đề này là liên kết tất cả cuộc gọi lại của bạn với đối tượng của bạn bằng bindphương thức javascript .

Bạn có thể làm điều này với một phương thức được đặt tên,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Hoặc với một cuộc gọi lại ẩn danh

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Làm những việc này thay vì dùng đến để var self = thischo bạn thấy sự ràng buộc củathis hành vi trong javascript và không dựa vào một tham chiếu đóng.

Ngoài ra, toán tử mũi tên chất béo trong ES6 về cơ bản giống như một cuộc gọi .bind(this) hàm ẩn danh:

doCallback( () => {
  // You can reference "this" here now
});

Cá nhân tôi nghĩ .bind (cái này) chỉ là gõ nhiều hơn, thay đổi tham chiếu này cho tôi (hoặc bất cứ điều gì) và sau đó chỉ sử dụng nó thay vì làm cho mã sạch hơn. Mũi tên béo là tương lai tho.
davidbuttar

3

Tôi chưa sử dụng jQuery, nhưng trong một thư viện như Prototype, bạn có thể liên kết các hàm với một phạm vi cụ thể. Vì vậy, trong tâm trí, mã của bạn sẽ trông như thế này:

 $('#foobar').ready('click', this.doSomething.bind(this));

Phương thức liên kết trả về một hàm mới gọi phương thức ban đầu với phạm vi bạn đã chỉ định.


Tương tự như: $('#foobar').ready('click', this.doSomething.call(this));phải không?
Muers

Các bindphương pháp không làm việc trong các trình duyệt cũ, vì vậy sử dụng $.proxyphương pháp thay thế.
Guffa

2

Chỉ cần thêm vào điều này trong ES6 vì các hàm mũi tên, bạn không cần phải làm điều này vì chúng nắm bắt được thisgiá trị.


1

Tôi nghĩ rằng nó thực sự phụ thuộc vào những gì bạn sẽ làm trong doSomethingchức năng của bạn . Nếu bạn sẽ truy cập MyObjectcác thuộc tính bằng từ khóa này thì bạn phải sử dụng nó. Nhưng tôi nghĩ rằng đoạn mã sau đây cũng sẽ hoạt động nếu bạn không làm bất kỳ điều gì đặc biệt bằng cách sử dụng object(MyObject)các thuộc tính.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

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