Điều gì làm cơ sở cho thành ngữ JavaScript này: var self = this?


357

Tôi đã thấy những điều sau đây trong nguồn cho WebKit HTML 5 SQL Storage Notes Demo :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

Tác giả sử dụng self ở một số nơi (thân hàm) và cái này ở những nơi khác (thân của các hàm được định nghĩa trong danh sách đối số của các phương thức). Chuyện gì đang xảy ra vậy? Bây giờ tôi đã nhận thấy nó một lần, tôi sẽ bắt đầu nhìn thấy nó ở khắp mọi nơi chứ?


4
Đây là một tính năng ngôn ngữ JS được gọi là đóng cửa từ vựng. "
subZero

2
Bản sao có thể có của: var self = this? .
DavidRR

khái niệm về NÀY được giải thích rõ ràng ở đây scotch.io/@alZami/under Hiểu
-this-in-javascript

Ví dụ có liên quan trong câu trả lời này stackoverflow.com/a/20279485/5610569 (cho câu hỏi "Làm thế nào để truy cập chính xác thistrong một cuộc gọi lại?")
FluxLemur

Câu trả lời:


431

Xem bài viết này trên alistapart.com . (Ed: Bài viết đã được cập nhật kể từ khi được liên kết ban đầu)

selfđang được sử dụng để duy trì một tham chiếu đến bản gốc thisngay cả khi bối cảnh đang thay đổi. Đây là một kỹ thuật thường được sử dụng trong các trình xử lý sự kiện (đặc biệt là trong các bao đóng).

Chỉnh sửa: Lưu ý rằng việc sử dụng selfhiện không được khuyến khích khi window.selftồn tại và có khả năng gây ra lỗi nếu bạn không cẩn thận.

Những gì bạn gọi là biến không đặc biệt quan trọng. var that = this;là tốt, nhưng không có gì kỳ diệu về cái tên.

Các hàm được khai báo bên trong một ngữ cảnh (ví dụ: gọi lại, đóng) sẽ có quyền truy cập vào các biến / hàm được khai báo trong cùng phạm vi hoặc ở trên.

Ví dụ: một cuộc gọi lại sự kiện đơn giản:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});


Xuất hiện bài báo đó được sử dụngvar that = this;
Bob Stein

@BobStein Cảm ơn. Tôi sẽ cập nhật câu trả lời phù hợp.
Jonathan Fingerland

96

Tôi nghĩ rằng tên biến 'tự' không nên được sử dụng theo cách này nữa, vì các trình duyệt hiện đại cung cấp một biến toàn cụcself trỏ đến đối tượng toàn cầu của một cửa sổ bình thường hoặc WebWorker.

Để tránh nhầm lẫn và xung đột tiềm ẩn, bạn có thể viết var thiz = thishoặc var that = thisthay vào đó.


43
Tôi thường sử dụng_this
djheru

6
@djheru +1. đẹp hơn nhiều so với " that" (mà não tôi sẽ không bao giờ quen được).
o_o_o--

9
Tôi bắt đầu sử dụng "tôi" :)
Mopparthy Ravindranath

3
Cho đến khi các trình duyệt hiện đại bắt đầu cung cấp một biến toàn cục _this, hoặc tôi.
Beejor

10
Hoàn toàn không có vấn đề gì với việc sử dụng tên selfmiễn là bạn tuyên bố nó là một variable, nó sẽ phủ bóng toàn cầu. Tất nhiên, nếu bạn quên varthì nó cũng không hoạt động với bất kỳ tên nào khác.
Bergi

34

Vâng, bạn sẽ thấy nó ở khắp mọi nơi. Nó thường xuyên that = this;.

Xem làm thế nào selfđược sử dụng bên trong các chức năng được gọi bởi các sự kiện? Những người sẽ có bối cảnh riêng của họ, vì vậy selfđược sử dụng để giữ những thisgì đã đi vào Note().

Lý do selfvẫn có sẵn cho các hàm, mặc dù chúng chỉ có thể thực thi sau khi Note()hàm đã thực thi xong, là các hàm bên trong có được ngữ cảnh của hàm ngoài do đóng .


12
Đối với tôi, điểm chung là selfkhông có ý nghĩa đặc biệt. Cá nhân tôi thích sử dụng một var được đặt tên khác hơn là selfvì nó thường làm tôi bối rối, vì tôi mong đợi "bản thân" là một từ dành riêng. Vì vậy, tôi thích câu trả lời của bạn. Và trong ví dụ về OP, tôi thích var thisNote = thishoặc tương tự.
steve

@steve đồng ý, mặc dù tôi cố gắng tránh sử dụng tài liệu tham khảo này / tự nói chung vì chúng rất dễ vỡ về khả năng bảo trì.
mattLummus

28

Cũng cần lưu ý rằng có một mẫu Proxy thay thế để duy trì tham chiếu đến bản gốc thistrong cuộc gọi lại nếu bạn không thích var self = thisthành ngữ này.

Vì một hàm có thể được gọi với một bối cảnh cụ thể bằng cách sử dụng function.applyhoặc function.call, bạn có thể viết một trình bao bọc trả về một hàm gọi hàm của bạn với applyhoặc callsử dụng bối cảnh đã cho. Xem proxychức năng của jQuery để thực hiện mẫu này. Đây là một ví dụ về việc sử dụng nó:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFuncsau đó có thể được gọi và sẽ có phiên bản của bạn thislàm bối cảnh.


10

Như những người khác đã giải thích, var self = this;cho phép mã trong một bao đóng để quay lại phạm vi cha.

Tuy nhiên, giờ là 2018 và ES6 được hỗ trợ rộng rãi bởi tất cả các trình duyệt web chính. Thành var self = this;ngữ không hoàn toàn cần thiết như trước đây.

Bây giờ có thể tránh var self = this;thông qua việc sử dụng các chức năng mũi tên .

Trong trường hợp chúng ta sẽ sử dụng var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Bây giờ chúng ta có thể sử dụng chức năng mũi tên mà không cần var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

Các hàm mũi tên không có riêng thisvà chỉ đơn giản là giả sử phạm vi kèm theo.


Hoặc - sốc, kinh hoàng! - tại sao không chuyển điều thực sự có liên quan làm đối số cho hàm của bạn (bao đóng)? Tại sao bạn lại tham khảo ngoài phạm vi, tại sao mọi người lại lập trình như thế này? Không bao giờ có bất kỳ lý do thực sự để làm điều này. Thay vào đó, .addEventListender("click", (x) => { console.log(x); });bạn đã giải thích cách thức và lý do rất rõ ràng và tôi đồng ý sử dụng các hàm mũi tên có ý nghĩa hơn, nhưng vẫn ... đây chỉ là lập trình khủng khiếp, lười biếng, lộn xộn.
Benjamin R

2
Trong JavaScript, việc quay lại phạm vi cha là cực kỳ phổ biến và cần thiết. Đó là một phần cơ bản của ngôn ngữ. Đề xuất của bạn để vượt qua trong phạm vi cha mẹ như là một đối số trên trình xử lý sự kiện không thực sự có thể. Ngoài ra, trong ES6, các hàm mũi tên sử dụng phạm vi từ vựng - 'cái này' đề cập đến phạm vi xung quanh hiện tại và không hơn thế - nó không "tham chiếu ngoài trạng thái phạm vi" hoặc bất cứ điều gì tương tự.
Elliot B.

9

Biến được bắt bởi các hàm nội tuyến được định nghĩa trong phương thức. thistrong hàm sẽ tham chiếu đến một đối tượng khác. Bằng cách này, bạn có thể làm cho hàm giữ tham chiếu đến thisphạm vi bên ngoài.


9

Đó là một cách giải quyết JavaScript. Khi một hàm là một thuộc tính của một đối tượng, được gọi là phương thức một cách thông minh hơn, điều này đề cập đến đối tượng. Trong ví dụ về một trình xử lý sự kiện, đối tượng chứa là phần tử kích hoạt sự kiện. Khi một chức năng tiêu chuẩn được gọi, điều này sẽ đề cập đến đối tượng toàn cầu. Khi bạn có các hàm lồng nhau như trong ví dụ của bạn, điều này không liên quan đến bối cảnh của hàm ngoài. Các hàm bên trong chia sẻ phạm vi với hàm chứa, vì vậy các nhà phát triển sẽ sử dụng các biến thể của var that = thisđể bảo toàn cái này họ cần trong hàm bên trong.


5

Thực tế bản thân là một tham chiếu đến window ( window.self) do đó khi bạn nói var self = 'something'bạn ghi đè một tham chiếu cửa sổ vào chính nó - bởi vì bản thân tồn tại trong đối tượng cửa sổ.

Đây là lý do tại sao hầu hết các nhà phát triển thích var that = thishơnvar self = this;

Dù sao; var that = this;không phù hợp với thông lệ tốt ... giả sử rằng mã của bạn sẽ được sửa đổi / sửa đổi sau bởi các nhà phát triển khác, bạn nên sử dụng các tiêu chuẩn lập trình phổ biến nhất đối với cộng đồng nhà phát triển

Do đó, bạn nên sử dụng một cái gì đó như var oldThis/ var oThis/ etc - để rõ ràng trong phạm vi của bạn // .. không nhiều lắm nhưng sẽ tiết kiệm được vài giây và vài chu kỳ não


2
@p Warrior Tôi nghĩ nó có ý nghĩa cho đến đoạn cuối cùng.
miphe

0

Như đã đề cập nhiều lần ở trên, 'cái tôi' chỉ đơn giản là được sử dụng để giữ một tham chiếu đến 'cái này' trước khi bước vào chức năng. Khi ở trong chức năng 'này' đề cập đến một cái gì đó khác.


@JohnPaul ... điều này không cung cấp câu trả lời. Nó có thể không phải là câu trả lời đúng, nhưng làm thế nào bạn có thể nói rằng "nó đang được sử dụng để ..." không phải là một câu trả lời cho "tại sao anh ta làm điều này"?
GreenAsJade

-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);


1
Bạn nên thêm một số giải thích với mã rằng những gì bạn đã làm đặc biệt.
Farhana
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.