Sự khác biệt giữa cái này và cái tôi trong JavaScript


118

Mọi người đều biết về thisjavascript, nhưng cũng có những trường hợp selfgặp phải trong tự nhiên, chẳng hạn như ở đây

Vậy, sự khác biệt giữa thisselftrong JavaScript là gì?



8
@dystroy: Có một cái: window.self( === window). Mặc dù OP có thể có nghĩa là một tên biến tầm thường…
Bergi

2
@dystroy: Thực ra tôi không nghĩ anh ấy có thể thực sự có ý đó, nhưng thực sự trong phạm vi toàn cầu (và môi trường trình duyệt) this === selfthì đúng :-)
Bergi

2
Chủ quan sang một bên: răng cưa thisđể selfkhông phải là một thực tế rất lớn hiện nay khi nó phổ biến để có mã với nhiều (tốt, nhiều hơn một là đủ xấu) mức độ gọi lại làm tổ, như một hệ quả của chương trình không đồng bộ. Thay vào đó, hãy sử dụng một tên mô tả hơn. Nói một cách khách quan, thisbản thân cái tên không mang thông tin gì và chỉ là một sự lựa chọn không phải tên vì ngữ cảnh từ vựng của định nghĩa lớp đủ điều kiện cho nó.
millimoose

2
đây là một câu hỏi hợp lệ và hữu ích, nó nên được mở lại
danza

Câu trả lời:


127

Trừ khi được đặt ở nơi khác, giá trị của selfwindowbởi vì JavaScript cho phép bạn truy cập vào bất kỳ xthuộc tính nào của nó một windowcách đơn giản x, thay vì window.x. Do đó, selfthực sự là window.self, khác với this.

window.self === window; // true

Nếu bạn đang sử dụng một hàm được thực thi trong phạm vi toàn cục và không ở chế độ nghiêm ngặt, hãy thismặc định thành window, và do đó

function foo() {
    console.log(
        window.self === window, // is self window?
        window.self === this,   // is self this?
        this === window         // is this window?
    );
}
foo(); // true true true

Nếu bạn đang sử dụng một hàm trong một ngữ cảnh khác, thissẽ tham chiếu đến ngữ cảnh đó, nhưng selfvẫn sẽ như vậy window.

// invoke foo with context {}
foo.call({}); // true false false

Bạn có thể tìm thấy window.selfđịnh nghĩa trong bản thảo làm việc W3C 2006 cho Đối tượng Cửa sổ tại đây .


34
Để hoàn thiện, selfrất hữu ích trong ngữ cảnh của WebWorker khi không thể truy cập được cửa sổ ( developer.mozilla.org/en-US/docs/Web/Guide/Performance/… ). Sử dụng selfthay vì windowcho phép bạn truy cập đối tượng toàn cục theo cách di động.
lqc

24

Một chút bổ sung cho điều này vì mọi người có thể gặp phải điều này trong ngữ cảnh của nhân viên dịch vụ, trong trường hợp đó, nó có nghĩa là một cái gì đó hơi khác.

Bạn có thể thấy điều này trong mô-đun service worker:

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
});

Ở đây tự đề cập đến WorkerGlobalScope và đây là phương pháp tiêu chuẩn để thiết lập trình nghe sự kiện.

Từ tài liệu Mozilla :

Bằng cách sử dụng self, bạn có thể tham chiếu đến phạm vi toàn cục theo cách sẽ hoạt động không chỉ trong ngữ cảnh cửa sổ (self sẽ phân giải thành window.self) mà còn trong ngữ cảnh worker (sau đó self sẽ phân giải thành WorkerGlobalScope.self).


Cảm ơn ! Tôi đang tìm kiếm câu trả lời này :)
agpt

23

Mặc dù tôi đến muộn ở đây nhưng tôi đã xem qua một ví dụ cũng có thể hữu ích để hiểu thisthêm:

var myObject = {
 foo: "bar",
 func: function() {
    var self = this;
    console.log("outer func:  this.foo = " + this.foo);
    console.log("outer func:  self.foo = " + self.foo);
    (function() {
        console.log("inner func:  this.foo = " + this.foo);
        console.log("inner func:  self.foo = " + self.foo);
    }());
  }
};
myObject.func();

O / P

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

Trước ECMA 5, thistrong hàm bên trong sẽ tham chiếu đến đối tượng cửa sổ toàn cục; trong khi, kể từ ECMA 5, thistrong hàm bên trong sẽ không được xác định.


điều này luôn được xác định trong ngữ cảnh của nó. Điều không xác định là this.foo. Đó là một sự khác biệt rất lớn và để đạt được hành vi mà bạn đã đề cập đã tồn tại trước ECMA 5, các hàm mũi tên có thể được sử dụng hoặc khi bạn chỉ định gán self là hàm này bên ngoài hàm bên trong và sử dụng tự bên trong thay vì hàm này, cách tốt hơn là hàm mũi tên .
Dejazmach

2

Cần làm rõ tham chiếu đến ECMA 5.

Tôi cho rằng nó có nghĩa là ECMA-262 Edition 5. Cần lưu ý rằng ECMA-262 (hay còn gọi là ECMAScript hoặc, chính xác hơn là Javascript) là ngôn ngữ kịch bản chung đã được triển khai trong Trình duyệt Internet. Từ tiêu chuẩn Edition 5.1:

Các bước sau được thực hiện khi điều khiển nhập vào ngữ cảnh thực thi cho mã hàm có trong đối tượng hàm F, một người gọi cung cấp thisArg và một người gọi cung cấp danh sách đối số:

  1. Nếu mã chức năng là mã nghiêm ngặt, hãy đặt ThisBinding thành thisArg.
  2. Nếu không nếu thisArg là null hoặc không xác định, hãy đặt ThisBinding thành đối tượng toàn cục.
  3. Khác nếu Loại (thisArg) không phải là Đối tượng, hãy đặt ThisBinding thành ToObject (thisArg).
  4. Khác đặt ThisBinding thành thisArg
  5. ... (không phải về "cái này")

Thuật ngữ "đối tượng toàn cầu" đề cập đến bất kỳ đối tượng nào ở trên cùng của chuỗi phạm vi. Đối với các trình duyệt, đây sẽ là đối tượng "cửa sổ" nhưng đây là lựa chọn triển khai (Máy chủ Windows Script có một đối tượng toàn cục vô hình nhưng không có chế độ nghiêm ngặt nên các tham chiếu không đủ điều kiện truy cập thuộc tính của nó và không có "tự" toàn cầu). Ngoài ra, "chế độ nghiêm ngặt" phải được bật rõ ràng nếu không nó không hoạt động (mục 14.1 của tiêu chuẩn). Do đó, "this" không xác định sẽ vẫn phân giải đối tượng toàn cục (cửa sổ) trong "ECMA 5" với chế độ nghiêm ngặt không hoạt động.

Vì vậy, câu trả lời cho câu hỏi là:

"this" luôn đề cập đến đối tượng gọi hàm. Nếu hàm không được gọi bởi một đối tượng (tức là không phải là một cuộc gọi phương thức) thì "this" (như được truyền cho hàm) là "không xác định". Tuy nhiên, nếu KHÔNG sử dụng chế độ nghiêm ngặt thì "this" không xác định được đặt thành đối tượng toàn cục (quy tắc 2 ở trên).

"self" không có ý nghĩa cú pháp đặc biệt, nó chỉ là một định danh. Các trình duyệt có xu hướng định nghĩa window.self (chỉ là một thuộc tính của đối tượng cửa sổ chung) = window. Điều này dẫn đến các tham chiếu không đủ tiêu chuẩn đến "self" giống với "window" UNLESS "self" đã được xác định lại trong một phạm vi kèm theo (chẳng hạn như "var self = this;" ở trên. Chúc bạn may mắn khi xác định lại "this".)

Vì vậy, lời giải thích đầy đủ của ví dụ trên là:

outer func:  this.foo = bar
// "this" refers to the invoking object "myObject"
outer func:  self.foo = bar
// "self" resolves to the variable in the local scope which has been set to "this" so it is also "myObject"
inner func:  this.foo = undefined
// "this" refers to the invoking object (none) and so is replaced by the global object (strict mode must be off). "window" has no foo property so its "value" is undefined.
inner func:  self.foo = bar
// self resolves to the variable in the enclosing scope which is still "myObject"

Một biến thể thú vị của ví dụ này tạo ra một bao đóng bằng cách trả về một tham chiếu đến hàm bên trong.

var myObject = {
 foo: "bar",
 func: function() {
    var self = this;
    console.log("outer func:  this.foo = " + this.foo);
    console.log("outer func:  self.foo = " + self.foo);
    return function() {
        console.log("inner func:  this.foo = " + this.foo);
        console.log("inner func:  self.foo = " + self.foo);
    };
  }
};
var yourObject = {
 foo: "blat",
 func: myObject.func() // function call not function object
};
console.log("----");
yourObject.func();

Sản xuất

outer func:  this.foo = bar
outer func:  self.foo = bar
----
inner func:  this.foo = blat
inner func:  self.foo = bar

Lưu ý cách hàm bên trong không được gọi cho đến khi được gọi bởi yourObject. Vì vậy this.foo bây giờ là yourObject.foo nhưng bản thân vẫn phân giải thành biến trong phạm vi bao quanh, tại thời điểm đối tượng hàm bên trong được trả về, là (và trong kết quả đóng vẫn là) myObject. Vì vậy, trong hàm bên trong, "this" đề cập đến đối tượng gọi hàm bên trong trong khi "bản thân" đề cập đến đối tượng được gọi là hàm bên ngoài để tạo tham chiếu đến hàm bên trong.

Để tóm tắt tóm tắt của tóm tắt, "cái này" được định nghĩa bởi tiêu chuẩn ngôn ngữ, "bản thân" được định nghĩa bởi bất kỳ ai định nghĩa nó (người thực hiện thời gian chạy hoặc người lập trình kết thúc).


0

Tìm bên dưới một số kết hợp của bảng điều khiển 'window', 'self' và 'this' xuất ra trong phạm vi toàn cầu (môi trường trình duyệt) để xem nó đang đề cập đến đâu.

console.log( window ); // Window {…}
console.log( self );   // Window {…}
console.log( this );   // Window {…}

console.log( window.window ); // Window {…}
console.log( window.self );   // Window {…}
console.log( window.this );   // undefined  

console.log( self.self );     // Window {…}
console.log( self.window );   // Window {…}
console.log( self.this );     // undefined

console.log( this.this );     // undefined
console.log( this.window );   // Window {…}
console.log( this.self );     // Window {…}

console.log( window.window.window );    // Window {…}
console.log( self.self.self );          // Window {…}
console.log( window.self.window.self ); // Window {…}
console.log( self.window.self.window ); // Window {…}
console.log( this.this );               // undefined
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.