Co leży u podstaw tego idiomu JavaScript: var self = this?

357

W źródle prezentacji WebKit HTML 5 SQL Storage Notes zobaczyłem następujące informacje :

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

Autor używa self w niektórych miejscach (ciało funkcji), a to w innych miejscach (ciała funkcji zdefiniowanych na liście argumentów metod). Co się dzieje? Teraz, kiedy to zauważyłem, czy zacznę widzieć wszędzie?

Thomas L. Holaday
źródło
4
Jest to funkcja języka JS o nazwie „zamknięcie leksykalne”
subZero
2
Możliwy duplikat: var self = this? .
DavidRR
pojęcie TO jest wyjaśnione wyraźnie tutaj scotch.io/@alZami/understanding-this-in-javascript
AL-zami 10.09.17
Odpowiedni przykład w tej odpowiedzi stackoverflow.com/a/20279485/5610569 (na pytanie „Jak uzyskać dostęp do poprawnego thiswywołania zwrotnego?”)
FluxLemur

Odpowiedzi:

431

Zobacz ten artykuł na alistapart.com . (Ed: artykuł został zaktualizowany od czasu pierwotnego połączenia)

selfjest używany do zachowania odniesienia do oryginału, thisnawet gdy zmienia się kontekst. Jest to technika często stosowana w procedurach obsługi zdarzeń (szczególnie w zamknięciach).

Edycja: Zauważ, że używanie selfjest odradzane, ponieważ window.selfistnieje i może powodować błędy, jeśli nie będziesz ostrożny.

To, co nazywamy zmienną, nie ma szczególnego znaczenia. var that = this;jest w porządku, ale w nazwie nie ma nic magicznego.

Funkcje zadeklarowane w kontekście (np. Wywołania zwrotne, zamknięcia) będą miały dostęp do zmiennych / funkcji zadeklarowanych w tym samym zakresie lub powyżej.

Na przykład proste wywołanie zwrotne zdarzenia:

function MyConstructor(options) {
  let that = this;

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

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

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

Jonathan Fingland
źródło
Wydaje się, że artykuł zmienił się w użycievar that = this;
Bob Stein
@BobStein Thanks. Odpowiednio zaktualizuję odpowiedź.
Jonathan Fingland
96

Myślę, że nazwa zmiennej „ja” nie powinna być już używana w ten sposób, ponieważ współczesne przeglądarki zapewniają zmienną globalnąself wskazującą na globalny obiekt zwykłego okna lub WebWorkera.

Aby uniknąć zamieszania i potencjalnych konfliktów, możesz napisać var thiz = thislub var that = thiszamiast tego.

Duan Yao
źródło
43
Zwykle używam_this
djheru
6
@djheru +1. o wiele ładniejsze niż „ that” (do którego mój mózg nigdy się nie przyzwyczai).
o_o_o--
9
Zacząłem używać „mnie” :)
Mopparthy Ravindranath
3
Aż współczesne przeglądarki zaczną dostarczać zmienną globalną - to, to lub ja.
Beejor,
10
Nie ma absolutnie żadnego problemu z używaniem nazwy, selfo ile zadeklarujesz ją jako varzdolną do zmiany, będzie ona śledzić globalne. Oczywiście, jeśli zapomnisz, varto też nie będzie działać z żadną inną nazwą.
Bergi,
34

Tak, zobaczysz to wszędzie. Jest to często that = this;.

Zobacz, jak selfużywane są funkcje wywoływane przez zdarzenia? Miałyby one swój własny kontekst, więc selfsą używane do przechowywania tego, thisco weszło Note().

Przyczyny selfsą nadal dostępne dla funkcji, mimo że mogą one zostać uruchomione dopiero po Note()zakończeniu wykonywania funkcji, jest to, że funkcje wewnętrzne uzyskują kontekst funkcji zewnętrznej z powodu zamknięcia .

Nosredna
źródło
12
Dla mnie istotnym punktem jest to, że selfnie ma specjalnego znaczenia. Osobiście wolę używać var ​​o nazwie coś innego niż selfto, że często mnie dezorientuje, ponieważ spodziewam się, że „ja” będzie słowem zastrzeżonym. Więc podoba mi się twoja odpowiedź. I w przykładzie OP wolałbym var thisNote = thislub podobnie.
steve
@steve zgodził się, chociaż staram się unikać korzystania z tego / własnych odniesień w ogóle, ponieważ są one bardzo kruche pod względem łatwości konserwacji.
mattLummus
28

Należy również zauważyć, że istnieje alternatywny wzorzec proxy do utrzymywania odniesienia do oryginału thisw wywołaniu zwrotnym, jeśli nie podoba ci się ten var self = thisidiom.

Ponieważ funkcja może zostać wywołana z danym kontekstem za pomocą function.applylub function.call, możesz napisać opakowanie, które zwraca funkcję, która wywołuje twoją funkcję za pomocą applylub callza pomocą podanego kontekstu. Zobacz proxyfunkcję jQuery dla implementacji tego wzorca. Oto przykład użycia:

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

wrappedFuncmoże zostać następnie wywołany i będzie miał twoją wersję thisjako kontekst.

Max
źródło
10

Jak wyjaśnili inni, var self = this;pozwala kodowi w zamknięciu odwoływać się z powrotem do zakresu nadrzędnego.

Jednak teraz jest 2018 i ES6 jest szeroko obsługiwany przez wszystkie główne przeglądarki internetowe. Thevar self = this; idiom nie jest tak niezbędny jak kiedyś.

Można teraz tego uniknąć var self = this;dzięki funkcjom strzałek .

W przypadkach, w których użylibyśmy var self = this:

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

Możemy teraz używać funkcji strzałki bez var self = this:

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

Funkcje strzałek nie mają własnych thisi po prostu przyjmują obejmujący zakres.

Elliot B.
źródło
Lub - szok, horror! - dlaczego nie przekazać rzeczywistej istotnej rzeczy jako argumentu do funkcji (zamknięcie)? Dlaczego do diabła odwołujesz się do stanu poza zakresem, dlaczego, do diabła, ktoś tak programuje? Nigdy nie ma żadnego prawdziwego powodu, aby to zrobić. Zamiast .addEventListender("click", (x) => { console.log(x); });tego bardzo jasno wyjaśniłeś, jak i dlaczego, i zgadzam się, że używanie funkcji strzałek ma większy sens, ale nadal ... to jest po prostu okropne, leniwe, niechlujne programowanie.
Benjamin R
2
W JavaScript odwoływanie się do zakresu nadrzędnego jest niezwykle powszechne i konieczne. To podstawowa część języka. Twoja sugestia przekazania w zakresie nadrzędnym jako argumentu modułu obsługi zdarzeń nie jest w rzeczywistości możliwa. Ponadto w ES6 funkcje strzałek używają zakresu leksykalnego - „to” odnosi się do jego obecnego zakresu i nie ma dalej - nie oznacza „odwoływania się do stanu poza zakresem” ani nic podobnego.
Elliot B.
9

Zmienna jest przechwytywana przez funkcje wbudowane zdefiniowane w metodzie. thisw funkcji odniesie się do innego obiektu. W ten sposób możesz sprawić, by funkcja zawierała odwołanie do thiszewnętrznego zakresu.

Mehrdad Afshari
źródło
9

To dziwactwo z JavaScript. Kiedy funkcja jest właściwością obiektu, chętniej zwana metoda, to odnosi się do obiektu. W przykładzie obsługi zdarzenia obiekt zawierający jest elementem, który wywołał zdarzenie. Przy standardowym funkcja jest wywoływana, to będzie odnosić się do globalnego obiektu. Gdy masz zagnieżdżone funkcje jak w przykładzie, to nie odnosi się do kontekstu funkcji zewnętrznej w ogóle. Funkcje wewnętrzne współdzielą zakres z funkcją zawierającą, więc programiści użyją odmian var that = this, aby zachować to, czego potrzebują w funkcji wewnętrznej.

kombat
źródło
5

W rzeczywistości self jest odniesieniem do okna (window.self ), dlatego kiedy mówiszvar self = 'something' że przesłonisz odwołanie do okna dla siebie - ponieważ self istnieje w obiekcie window.

To dlatego większość deweloperzy wolą var that = thisponadvar self = this;

Tak czy siak; var that = this;nie jest zgodne z dobrą praktyką ... zakładając, że Twój kod zostanie później zmieniony / zmodyfikowany przez innych programistów, powinieneś używać najpopularniejszych standardów programistycznych w odniesieniu do społeczności programistów

Dlatego powinieneś użyć czegoś takiego jak var oldThis/ var oThis/ etc - aby być jasnym w swoim zasięgu // .. to nie tyle, ale zaoszczędzi kilka sekund i kilka cykli mózgowych

Sorin N.
źródło
2
@prior Myślę, że ma to sens aż do ostatniego akapitu.
miphe
0

Jak wspomniano kilka razy powyżej, „jaźń” jest po prostu używane do zachowania odniesienia do „tego” przed wejściem do funkcji. Raz w funkcji „to” odnosi się do czegoś innego.

Cyprien
źródło
@JohnPaul ... to ma stanowić odpowiedź. Być może nie jest to właściwa odpowiedź, ale jak można powiedzieć, że „przyzwyczaja się ...” nie jest odpowiedzią na pytanie „dlaczego to zrobił”?
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);

ht zhao
źródło
1
Powinieneś dodać wyjaśnienie z kodem, że zrobiłeś coś specjalnego.
Farhana,