var self = to?

182

Użycie metod instancji jako wywołań zwrotnych dla procedur obsługi zdarzeń zmienia zakres thisz „Moje wystąpienie” na „Cokolwiek, co właśnie wywołało wywołanie zwrotne” . Mój kod wygląda tak

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

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

Działa, ale czy to najlepszy sposób, aby to zrobić? Wygląda mi to dziwnie.

defnull
źródło
15
Powinieneś unikać, selfponieważ istnieje jakiś window.selfobiekt i możesz go użyć przypadkowo, jeśli zapomnisz zadeklarować swój własny selfvar (np. Podczas przenoszenia jakiegoś kodu). Może to być denerwujące w wykrywaniu / debugowaniu. Lepiej użyć czegoś takiego _this.
Alastair Maw
3
Z samouczka JavaScript : „Wartość thisJavaScript jest dynamiczna w JavaScript. Jest określana, kiedy funkcja jest wywoływana , a nie kiedy jest deklarowana”.
DavidRR
Chodzi o to, że w zakresie globalnym self === this. Dlatego selfw lokalnych kontekstach ma sens i jest zgodny z tym wzorcem.
programator5000

Odpowiedzi:

210

To pytanie nie jest specyficzne dla jQuery, ale ogólnie dotyczy JavaScript. Podstawowym problemem jest sposób „channelowania” zmiennej we wbudowanych funkcjach. Oto przykład:

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

Ta technika polega na użyciu zamknięcia. Ale to nie działa, thisponieważ thisjest pseudo zmienną, która może dynamicznie zmieniać się z zakresu na zakres:

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

Co możemy zrobić? Przypisz ją do jakiejś zmiennej i użyj jej przez alias:

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

thisnie jest pod tym względem wyjątkowy: argumentsto inna pseudo-zmienna, którą należy traktować w ten sam sposób - poprzez aliasing.

Eugene Lazutkin
źródło
7
Naprawdę powinieneś „powiązać” to z wywołaniem funkcji, a nie przekazywać go ani przechodzić z nim zakresów zmiennych
Michael
10
@ Michael Dlaczego?
Chris Anderson,
@ Michael, możesz pracować wewnątrz potoku, mapy, subskrybuje ... w jednej funkcji i nie wywołujesz funkcji, w tym przypadku „to” się zmienia, więc rozwiązaniem jest zmienny zakres
Skander Jenhani
@SkanderJenhani ten komentarz ma 8 lat. Moja odpowiedź byłaby dzisiaj zupełnie inna
Michael
18

Tak, to wydaje się być powszechnym standardem. Niektórzy koderzy używają siebie, inni używają mnie. Jest używany jako odniesienie do „prawdziwego” obiektu, w przeciwieństwie do zdarzenia.

Zajęło mi to trochę czasu, na początku wygląda dziwnie.

Zwykle robię to bezpośrednio na górze mojego obiektu (wybacz mój kod demo - jest bardziej konceptualny niż cokolwiek innego i nie jest lekcją doskonałej techniki kodowania):

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
  }
}
BenAlabaster
źródło
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.
  }
}

edycja: pomimo tego, zagnieżdżone funkcje w obiekcie przyjmują raczej globalny obiekt okna niż otaczający obiekt.

serkan
źródło
Wygląda na to var self = this, że opisujesz, co robi, ale nie o to pyta (pytanie dotyczy tego, czy to najlepsze rozwiązanie problemu).
Quentin,
3
„Podstawowym problemem jest to, jak„ kanałować ”zmienną we wbudowanych funkcjach.” Zgadzam się z tym stwierdzeniem w przyjętej odpowiedzi. Poza tym nie ma nic złego w „tej” praktyce. To jest dość powszechne. Tak więc, jako odpowiedź na część „wygląda mi dziwnie” postanowiłem wyjaśnić, jak to działa. Uważam, że powodem, dla którego „kod wygląda dla mnie dziwnie”, jest zamieszanie związane z jego działaniem.
serkan
12

Jeśli wykonujesz ES2015 lub skrypt typu i ES5, możesz użyć funkcji strzałek w kodzie i nie napotykasz tego błędu, a to odnosi się do pożądanego zakresu w Twojej instancji.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});
Aran Dekar
źródło
5
Jako wyjaśnienie: w ES2015 funkcje strzałek przechwytują thisz ich zdefiniowanego zakresu. Zwykłe definicje funkcji tego nie robią.
defnull
@defnull Nie sądzę, żeby to było coś specjalnego. Funkcje tworzą zakres blokowy. więc użycie notacji strzałkowej zamiast funkcji powstrzymuje cię od utworzenia zakresu, więc nadal będzie się odnosić do zewnętrznej tej zmiennej.
Nimeshka Srimal,
4

Jednym z rozwiązań jest powiązanie całego wywołania zwrotnego z obiektem za pomocą bindmetody javascript .

Możesz to zrobić za pomocą nazwanej metody,

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

doCallBack(MyNamedMethod.bind(this)); 

Lub z anonimowym oddzwonieniem

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

Wykonanie ich zamiast uciekania się do var self = thispokazuje, że rozumiesz, jak thiszachowuje się wiązanie w javascript i nie polega na odwołaniu do zamknięcia.

Ponadto operator grubej strzałki w ES6 jest w zasadzie tym samym wywołaniem .bind(this)anonimowej funkcji:

doCallback( () => {
  // You can reference "this" here now
});
Bart Dorsey
źródło
Osobiście uważam, że .bind (this) to po prostu więcej pisania, zmiana tego odniesienia na mnie (lub cokolwiek innego), a następnie użycie go zamiast tego zapewnia czystszy kod. Gruba strzała to przyszłość.
davidbuttar
3

Nie korzystałem z jQuery, ale w bibliotece takiej jak Prototype możesz powiązać funkcje z określonym zakresem. Mając to na uwadze, Twój kod wyglądałby tak:

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

Metoda bind zwraca nową funkcję, która wywołuje oryginalną metodę o określonym zakresie.

neonski
źródło
Analogicznie do: $('#foobar').ready('click', this.doSomething.call(this));prawda?
Muers,
Ta bindmetoda nie działa w starszych przeglądarkach, więc $.proxyzamiast tego użyj tej metody.
Guffa
2

Wystarczy dodać do tego, że w ES6 z powodu funkcji strzałek nie powinieneś tego robić, ponieważ przechwytują thiswartość.

sierpień
źródło
1

Myślę, że tak naprawdę zależy to od tego, co zamierzasz zrobić w ramach swojej doSomethingfunkcji. Jeśli chcesz uzyskać dostęp do MyObjectwłaściwości za pomocą tego słowa kluczowego, musisz go użyć. Ale myślę, że poniższy fragment kodu będzie działał również, jeśli nie robisz żadnych specjalnych rzeczy za pomocą object(MyObject)właściwości.

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

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

});
Scott Harrison
źródło