Jak uzyskać dostęp do prawidłowego „this” wewnątrz oddzwaniania?

1425

Mam funkcję konstruktora, która rejestruje moduł obsługi zdarzeń:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

Nie mogę jednak uzyskać dostępu do datawłaściwości utworzonego obiektu w wywołaniu zwrotnym. Wygląda na to, thisże nie odnosi się do obiektu, który został stworzony, ale do innego.

Próbowałem także użyć metody obiektowej zamiast funkcji anonimowej:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

ale wykazuje te same problemy.

Jak mogę uzyskać dostęp do właściwego obiektu?

Felix Kling
źródło
95
Od czasu do czasu mam tyle pytań, że postanawiam napisać odpowiedź kanoniczną. Chociaż na te pytania udzielono odpowiedzi milion razy, nie zawsze jest możliwe znalezienie dobrej pary pytanie + odpowiedź, która nie jest „zanieczyszczona” nieistotnymi informacjami. To jedna z tych chwil i jedno z tych pytań (i nudzę się). Jeśli uważasz, że istnieje naprawdę dobre kanoniczne pytanie / odpowiedź na tego typu pytanie, daj mi znać, a ja je usunę. Sugestie dotyczące ulepszeń są mile widziane!
Felix Kling
3
Przydatna strona TypeScript na ten temat , głównie dotyczy również JS.
Ondra Žižka

Odpowiedzi:

1790

Co powinieneś wiedzieć this

this(aka „kontekst”) jest specjalnym słowem kluczowym wewnątrz każdej funkcji, a jej wartość zależy tylko od tego, jak funkcja została wywołana, a nie jak / kiedy / gdzie została zdefiniowana. Nie ma na to wpływu zakresy leksykalne, takie jak inne zmienne (z wyjątkiem funkcji strzałek, patrz poniżej). Oto kilka przykładów:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

Aby dowiedzieć się więcej, zapoznaj się thisz dokumentacją MDN .


Jak odnieść się do prawidłowego this

Nie używaj this

W rzeczywistości nie chcesz thisw szczególności uzyskiwać dostępu , ale obiekt, do którego się odnosi . Dlatego łatwym rozwiązaniem jest po prostu utworzenie nowej zmiennej, która również odnosi się do tego obiektu. Zmienna może mieć dowolną nazwę, ale typowymi są selfi that.

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

Ponieważ selfjest to normalna zmienna, jest zgodna z zasadami zakresu leksykalnego i jest dostępna w wywołaniu zwrotnym. Ma to również tę zaletę, że można uzyskać dostęp do thiswartości samego wywołania zwrotnego.

Jawnie ustawiony zestaw thiszwrotny - część 1

Może się wydawać, że nie masz kontroli nad wartością, thisponieważ jego wartość jest ustawiana automatycznie, ale tak naprawdę nie jest.

Każda funkcja ma metodę .bind [docs] , która zwraca nową funkcję thispowiązaną z wartością. Ta funkcja działa dokładnie tak samo jak ta, którą wywołałeś .bind, tyle że thiszostała ustawiona przez ciebie. Bez względu na to, jak i kiedy ta funkcja jest wywoływana, thiszawsze będzie odnosić się do przekazanej wartości.

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

W takim przypadku wiążemy wywołania zwrotne thisz wartością MyConstructor's this.

Uwaga: Podczas wiązania kontekstu dla jQuery użyj zamiast tego jQuery.proxy [docs] . Powodem tego jest to, że nie trzeba przechowywać odwołania do funkcji podczas rozpakowywania wywołania zwrotnego zdarzenia. jQuery obsługuje to wewnętrznie.

ECMAScript 6: Użyj funkcji strzałek

ECMAScript 6 wprowadza funkcje strzałek , które można traktować jako funkcje lambda. Nie mają własnego thiswiązania. Zamiast tego thisjest sprawdzany w zakresie jak normalna zmienna. Oznacza to, że nie musisz dzwonić .bind. To nie jedyne specjalne zachowanie, jakie mają. Więcej informacji można znaleźć w dokumentacji MDN.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

Zestaw thisoddzwaniania - część 2

Niektóre funkcje / metody, które akceptują wywołania zwrotne, akceptują również wartość, do której thispowinny się odnosić wywołania zwrotne . Jest to w zasadzie to samo, co samodzielne wiązanie, ale funkcja / metoda robi to za Ciebie. Array#map [docs] jest taką metodą. Jego podpis to:

array.map(callback[, thisArg])

Pierwszy argument to wywołanie zwrotne, a drugi argument to wartość, do której thisnależy się odwoływać. Oto wymyślony przykład:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

Uwaga: To, czy możesz przekazać wartość, thisjest zwykle wspomniane w dokumentacji tej funkcji / metody. Na przykład metoda jQuery [docs]$.ajax opisuje opcję o nazwie context:

Ten obiekt stanie się kontekstem wszystkich wywołań zwrotnych związanych z Ajax.


Typowy problem: używanie metod obiektowych jako wywołań zwrotnych / procedur obsługi zdarzeń

Innym częstym przejawem tego problemu jest użycie metody obiektowej jako funkcji obsługi wywołania zwrotnego / zdarzenia. Funkcje są pierwszorzędnymi obywatelami w JavaScript, a termin „metoda” jest tylko terminem potocznym dla funkcji, która jest wartością właściwości obiektu. Ale ta funkcja nie ma konkretnego łącza do jej „zawierającego” obiektu.

Rozważ następujący przykład:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

Funkcja this.methodjest przypisany jako obsługi zdarzenia kliknięcia, ale jeśli document.bodyzostanie kliknięty, wartość zalogowany będzie undefined, ponieważ wewnątrz obsługi zdarzeń, thisodnosi się do document.body, a nie instancji Foo.
Jak już wspomniano na początku, to, do czego się thisodnosi, zależy od tego, jak funkcja jest wywoływana , a nie jak jest zdefiniowana .
Jeśli kod wyglądał następująco, może być bardziej oczywiste, że funkcja nie ma niejawnego odwołania do obiektu:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

Rozwiązanie jest takie samo jak wspomniane powyżej: jeśli jest dostępne, użyj, .bindaby jawnie powiązać thisz określoną wartością

document.body.onclick = this.method.bind(this);

lub jawnie wywołać funkcję jako „metodę” obiektu, używając funkcji anonimowej jako funkcji obsługi wywołania zwrotnego / zdarzenia i przypisując obiekt ( this) do innej zmiennej:

var self = this;
document.body.onclick = function() {
    self.method();
};

lub użyj funkcji strzałki:

document.body.onclick = () => this.method();
Felix Kling
źródło
39
Felix, czytałem już tę odpowiedź, ale nigdy nie odpowiedziałem. Obawiam się, że ludzie używająself i thatdo których można się odwoływać this. Czuję się tak, ponieważ thisprzeciążona zmienna jest używana w różnych kontekstach; podczas gdy selfzwykle odpowiada lokalnej instancji i thatzwykle odnosi się do innego obiektu. Wiem, że nie ustawiłeś tej zasady, ponieważ widziałem, jak pojawia się ona w wielu innych miejscach, ale właśnie dlatego zacząłem ją stosować _this, ale nie jestem pewien, jak się czują inni, z wyjątkiem niejednolitej praktyki to spowodowało.
vol7ron
3
@ FelixKling czy można bezpiecznie założyć, że korzystanie z tej wewnętrznej funkcji prototypowej zawsze będzie miało oczekiwane zachowanie, niezależnie od tego, jak są (zazwyczaj) wywoływane? Czy podczas korzystania z funkcji zwrotnych w funkcjach prototypowych istnieje alternatywa dla bind (), self lub that?
andig
5
@FelixKling Czasami warto polegać na Function.prototype.call ()i Function.prototype.apply (). Szczególnie zapply () że mam duży przebieg. Jestem mniej skłonny do używania bind ()być może tylko z przyzwyczajenia, chociaż zdaję sobie sprawę (ale nie jestem pewien), że korzystanie z opcji wiązania w porównaniu z innymi opcjami może mieć niewielkie zalety.
Nolo,
5
Świetna odpowiedź, ale rozważ dodanie dodatkowego opcjonalnego rozwiązania, które po prostu nie korzysta z klas, nowych lub w ogóle.
Aluan Haddad
4
funkcje strzałki re „Zamiast tego wygląda to tak, jak normalna zmienna”. całkowicie kliknął dla mnie, dziękuję! () => this.clicked();)
alphanumeric0101
211

Oto kilka sposobów dostępu do kontekstu nadrzędnego w kontekście potomnym -

  1. Możesz użyć bind()funkcji.
  2. Odwołaj się do kontekstu / this w innej zmiennej (patrz przykład poniżej).
  3. Użyj funkcji strzałek ES6 .
  4. Zmień kod / funkcję / architekturę funkcji - w tym celu powinieneś mieć kontrolę nad wzorami projektowymi w javascript.

1. Użyj bind() funkcji

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', ( function () {
        alert(this.data);
    }).bind(this) );
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

Jeśli używasz underscore.js- http://underscorejs.org/#bind

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2 Zapisz odniesienie do kontekstu / tego w innej zmiennej

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}

3 Funkcja strzałki

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
Mohan Dere
źródło
1
Opcja bind () jest niesamowita, po prostu mija wskaźnik tego obiektu, aby być tym na innym obiekcie (: Dzięki!
Stav Bodik
Funkcja bind () działa jak urok. Dziękuję bardzo +1 ode mnie :)
Anjana Silva
56

Wszystko opiera się na „magicznej” składni wywołania metody:

object.property();

Kiedy pobierzesz właściwość z obiektu i wywołasz ją za jednym razem, obiekt będzie kontekstem dla metody. Jeśli wywołasz tę samą metodę, ale w oddzielnych krokach, kontekstem będzie zasięg globalny (okno):

var f = object.property;
f();

Kiedy otrzymasz referencję do metody, nie jest ona już dołączona do obiektu, jest to tylko odwołanie do prostej funkcji. To samo dzieje się, gdy otrzymasz odwołanie do użycia jako oddzwonienia:

this.saveNextLevelData(this.setAll);

W tym miejscu powiąż kontekst z funkcją:

this.saveNextLevelData(this.setAll.bind(this));

Jeśli używasz jQuery, powinieneś użyć $.proxymetody, ponieważ bindnie jest obsługiwana we wszystkich przeglądarkach:

this.saveNextLevelData($.proxy(this.setAll, this));
Guffa
źródło
33

Problem z „kontekstem”

Termin „kontekst” jest czasem używany w odniesieniu do obiektu, do którego się to odnosi . Jego zastosowanie jest niewłaściwe, ponieważ nie pasują zarówno semantycznie lub technicznie z ECMAScript na to .

„Kontekst” oznacza okoliczności otaczające coś, co dodaje znaczenie, lub niektóre informacje poprzedzające i następujące po nim, które nadają dodatkowe znaczenie. Termin „kontekst” jest używany w ECMAScript w odniesieniu do kontekstu wykonania , który obejmuje wszystkie parametry, zakres i to w zakresie części wykonywanego kodu.

Jest to pokazane w ECMA-262 sekcja 10.4.2 :

Ustaw ThisBinding na tę samą wartość, co ThisBinding kontekstu wykonywania wywołania

który wyraźnie wskazuje, że to jest częścią kontekstu wykonania.

Kontekst wykonania zapewnia otaczające informacje, które nadają znaczenie wykonywanemu kodowi. Zawiera znacznie więcej informacji niż tylko to wiązanie .

Zatem wartością tego nie jest „kontekst”, to tylko jedna część kontekstu wykonania. Zasadniczo jest to zmienna lokalna, którą można ustawić przez wywołanie dowolnego obiektu i w trybie ścisłym dowolnej wartości.

RobG
źródło
Nie mogę się zgodzić z tą odpowiedzią. Istnienie terminu „kontekst wykonania” nie zakazuje innych zastosowań „kontekstu” w większym stopniu niż zakazuje innych zastosowań „wykonania”. Być może istnieje lepszy termin do opisania, thisale żaden nie jest tu oferowany i prawdopodobnie jest już za późno, aby zamknąć drzwi w „kontekście”.
Roamer-1888
@ Roamer-1888 - dziękuję za edycję. Masz rację, ale mój argument nie opiera się na istnieniu „kontekstu wykonania” wykluczającego „kontekst” w innym celu. Raczej opiera się na tym, że „kontekst” jest niewłaściwy zarówno z technicznego, jak i semantycznego punktu widzenia. Myślę też, że użycie „kontekstu” zamiast „tego” wymiera. Nie widzę żadnego powodu, aby znaleźć alternatywne określenie dla tego lub tego wiązania , po prostu zaciemnia i oznacza, że ​​w pewnym momencie musisz wyjaśnić, że „kontekst” jest w rzeczywistości tym , i że w żadnym razie nie jest w „kontekście”. :-)
RobG
Nie sądzę, że można powiedzieć, że to nie jest w żaden sposób „kontekst”, gdy już admittted że jedna część kontekstu wykonania, gdzie „wykonanie” jest jedynie przymiotnikowy.
Roamer-1888,
@ Roamer-1888 - Nie zamierzam kontynuować tej rozmowy po tym punkcie. Tak, to jest część od kontekście wykonania. Mówienie, że jest to kontekst, przypomina mówienie, że jeden gracz z zespołu to zespół.
RobG
RobG, szkoda, że ​​nie chcesz kontynuować. To interesująca debata. Dziękuję za poświęcenie mi czasu.
Roamer-1888,
31

Powinieneś wiedzieć o „tym” słowie kluczowym.

Zgodnie z moim poglądem możesz to „wdrożyć” na trzy sposoby (Self / Funkcja strzałki / Metoda wiązania)

Funkcja to słowo kluczowe zachowuje się nieco inaczej w JavaScript w porównaniu do innych języków.

Ma także pewne różnice między trybem ścisłym a trybem ścisłym.

W większości przypadków wartość tego zależy od sposobu wywołania funkcji.

Nie można go ustawić przez przypisanie podczas wykonywania i może być inny przy każdym wywołaniu funkcji.

ES5 wprowadził metodę bind () w celu ustawienia wartości funkcji niezależnie od tego, jak się nazywa,

i ES2015 wprowadził funkcje strzałek, które nie zapewniają tego powiązania (zachowuje tę wartość otaczającego kontekstu leksykalnego).

Metoda 1: Jaźń jest używana do zachowania odniesienia do oryginału, nawet gdy zmienia się kontekst. Jest to technika często stosowana w procedurach obsługi zdarzeń (szczególnie w zamknięciach).

Odniesienie : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function () {
        alert(self.data);
    });
}

Metoda 2 : Funkcja strzałki - wyrażenie funkcji strzałki jest zwartą składniowo alternatywą dla wyrażenia funkcji regularnej,

chociaż bez własnych powiązań z tym, argumentami, super lub new.target słowami kluczowymi.

Wyrażenia funkcji strzałek źle nadają się na metody i nie można ich używać jako konstruktorów.

Odniesienie : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',()=> {
        alert(this.data);
    });
}

Method3 : Bind- Metoda bind () tworzy nową funkcję, która:

po wywołaniu ma to słowo kluczowe ustawione na podaną wartość,

z podaną sekwencją argumentów poprzedzającą dowolną podaną podczas wywoływania nowej funkcji.

Odniesienie: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',(function() {
        alert(this.data);
    }).bind(this);
Ashish
źródło
25

Po pierwsze, musisz dobrze rozumieć scopei zachowywać thissłowa kluczowe w kontekście scope.

thisI scope:


there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

w skrócie, zakres globalny odnosi się do obiektu okna. Zmienne zadeklarowane w zasięgu globalnym są dostępne z dowolnego miejsca. Z drugiej strony zakres funkcji znajduje się wewnątrz funkcji. zmienna zadeklarowana wewnątrz funkcji nie jest normalnie dostępna z zewnątrz. thissłowo kluczowe w zasięgu globalnym odnosi się do obiektu okna. thisfunkcja wewnętrzna odnosi się również do obiektu okna this, więc zawsze będzie odnosić się do okna, dopóki nie znajdziemy sposobu na manipulację w thiscelu wskazania kontekstu, który sam wybieramy.

--------------------------------------------------------------------------------
-                                                                              -
-   Global Scope                                                               -
-   ( globally "this" refers to window object)                                 -     
-                                                                              -
-         function outer_function(callback){                                   -
-                                                                              -
-               // outer function scope                                        -
-               // inside outer function"this" keyword refers to window object -                                                                              -
-              callback() // "this" inside callback also refers window object  -

-         }                                                                    -
-                                                                              -
-         function callback_function(){                                        -
-                                                                              -
-                //  function to be passed as callback                         -
-                                                                              -
-                // here "THIS" refers to window object also                   -
-                                                                              -
-         }                                                                    -
-                                                                              -
-         outer_function(callback_function)                                    -
-         // invoke with callback                                              -
--------------------------------------------------------------------------------

Różne sposoby manipulowania this funkcjami wywołania zwrotnego:

Tutaj mam funkcję konstruktora o nazwie Osoba. Posiada właściwość o nazwie namei cztery metody zwanej sayNameVersion1, sayNameVersion2,sayNameVersion3 , sayNameVersion4. Wszystkie cztery mają jedno konkretne zadanie. Zaakceptuj wywołanie zwrotne i wywołaj je. Oddzwonienie ma określone zadanie polegające na zarejestrowaniu właściwości name wystąpienia funkcji konstruktora Person.

function Person(name){

    this.name = name

    this.sayNameVersion1 = function(callback){
        callback.bind(this)()
    }
    this.sayNameVersion2 = function(callback){
        callback()
    }

    this.sayNameVersion3 = function(callback){
        callback.call(this)
    }

    this.sayNameVersion4 = function(callback){
        callback.apply(this)
    }

}

function niceCallback(){

    // function to be used as callback

    var parentObject = this

    console.log(parentObject)

}

Teraz stwórzmy instancję z konstruktora person i wywołajmy różne wersje metody sayNameVersionX(X odnosi się do 1,2,3,4), niceCallbackaby zobaczyć, na ile sposobów możemy manipulować thiswewnętrznym wywołaniem zwrotnym, aby odwoływać się do personinstancji.

var p1 = new Person('zami') // create an instance of Person constructor

wiążą:

Wiązanie polega na utworzeniu nowej funkcji za pomocą this słowem kluczowym ustawionym na podaną wartość.

sayNameVersion1i sayNameVersion2użyj bind do manipulowania thisfunkcją wywołania zwrotnego.

this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}

pierwszy wiąże się thisz wywołaniem zwrotnym w samej metodzie, a drugi jest przekazywany z powiązanym obiektem.

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

połączenie :

first argumentOd callsposobu stosuje się thiswewnątrz funkcji, które są wywoływane zcall nim związane.

sayNameVersion3służy calldo manipulowania, thisaby odwoływać się do obiektu osoby, który stworzyliśmy, zamiast obiektu okna.

this.sayNameVersion3 = function(callback){
    callback.call(this)
}

i nazywa się to następująco:

p1.sayNameVersion3(niceCallback)

zastosować :

Podobnie jak callpierwszy argument parametru applyodnosi się do obiektu, który będzie wskazywany przez thissłowo kluczowe.

sayNameVersion4służy applydo manipulowania thisodniesieniem do obiektu osoby

this.sayNameVersion4 = function(callback){
    callback.apply(this)
}

i nazywa się to następująco: po prostu wywołanie zwrotne zostało przekazane,

p1.sayNameVersion4(niceCallback)
AL-zami
źródło
1
każda konstruktywna krytyka dotycząca odpowiedzi zostanie doceniona!
AL-zami
1
To słowo kluczowe w zasięgu globalnym niekoniecznie odnosi się do obiektu okna . Dotyczy to tylko przeglądarki.
Randall Flagg
1
@RandallFlagg Napisałem tę odpowiedź z perspektywy przeglądarki. W razie potrzeby możesz ją przerobić :)
AL-zami
19

Nie możemy tego powiązać setTimeout(), ponieważ zawsze wykonuje się to za pomocą globalnego obiektu (Window) , jeśli chcesz uzyskać dostępthis kontekstu w funkcji wywołania zwrotnego, to używając bind()funkcji wywołania zwrotnego możemy osiągnąć jako:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);
Datta Chanewad
źródło
9
Czym różni się to od istniejących odpowiedzi?
Felix Kling
13

Pytanie dotyczy thiszachowania słowa kluczowego w javascript. thiszachowuje się inaczej niż poniżej,

  1. Wartość this jest zwykle określana przez kontekst wykonywania funkcji.
  2. W zakresie globalnym thisodnosi się do obiektu globalnego ( windowobiektu).
  3. Jeśli tryb ścisły jest włączona dla dowolnej funkcji, to wartość thisbędzie undefinedjak w trybie ścisłym, globalny obiekt odnosi się undefinedw miejscu windowobiektu.
  4. Obiekt stojący przed kropką jest związany z tym słowem kluczowym.
  5. Możemy ustawić wartość to wyraźnie z call(), bind()iapply()
  6. Kiedy newużywane jest słowo kluczowe (konstruktor), jest ono powiązane z tworzonym nowym obiektem.
  7. Funkcje strzałek nie wiążą się this - thissą powiązane leksykalnie (tzn. W oparciu o oryginalny kontekst)

Jak sugeruje większość odpowiedzi, możemy użyć funkcji strzałki lub bind()metody lub własnego var. Chciałbym zacytować punkt o lambdas (funkcja strzałki) z Przewodnika po stylu Google JavaScript

Wolę używać funkcji strzałek niż f.bind (this), a zwłaszcza goog.bind (f, this). Unikaj pisania const self = this. Funkcje strzałek są szczególnie przydatne w przypadku wywołań zwrotnych, które czasami przekazują nieoczekiwane dodatkowe argumenty.

Google wyraźnie zaleca stosowanie lambdas zamiast wiązania lub const self = this

Najlepszym rozwiązaniem byłoby użycie lambd jak poniżej,

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}

Bibliografia:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. arrow-functions-vs-bind
Code_Mode
źródło
To pytanie dotyczy w szczególności używania funkcji / metod jako wywołań zwrotnych. Twoja odpowiedź może lepiej pasować do stackoverflow.com/q/3127429/218196 .
Felix Kling
@ FelixKling Tak pytanie dotyczy używania funkcji / metod jako wywołań zwrotnych w tym głównym problemie z powodu obsługi thissłowa kluczowego, dlatego podzieliłem moją odpowiedź na dwie części, jedną o thisdrugiej, a drugą dotyczącą używania funkcji / metod jako wywołań zwrotnych. Edytuj odpowiedź.
Code_Mode
Czwarty punkt uważam za dwuznacznie sformułowany. Rozważ przykład „Problem z użyciem metod z tym obiektem jako wywołania zwrotne” , w którym właściwy obiekt stoi przed kropką, ale kontekst nie jest tym obiektem.
bleistift2
7

Obecnie możliwe jest inne podejście, jeśli klasy są używane w kodzie.

Dzięki obsłudze pól klasowych można to zrobić w następujący sposób:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)

Na pewno pod maską jest to stara dobra funkcja strzałki, która wiąże kontekst, ale w tej formie wygląda znacznie wyraźniej niż wyraźne wiązanie.

Ponieważ jest to propozycja etapu 3, będziesz potrzebować babel i odpowiedniej wtyczki babel do przetworzenia go jak na razie (08/2018).

skyboyer
źródło
2
Właśnie tak działam w Typescript: public methodName = (params) => { body }wewnątrz klasy.
yeyeyerman
5

Innym podejściem, które jest standardowym sposobem od wiązania przez DOM2this w detektorze zdarzeń, który pozwala zawsze usuwać detektor (oprócz innych korzyści), jest handleEvent(evt)metoda z EventListenerinterfejsu:

var obj = {
  handleEvent(e) {
    // always true
    console.log(this === obj);
  }
};

document.body.addEventListener('click', obj);

Szczegółowe informacje na temat korzystania handleEventmożna znaleźć tutaj: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

Andrea Puddu
źródło
0

this w JS:

Wartość thisw JS jest w 100% określona przez to, jak funkcja jest wywoływana, a nie jak jest zdefiniowana. Stosunkowo łatwo możemy znaleźć wartość reguły „this po lewej stronie kropki” :

  1. Gdy funkcja jest tworzona za pomocą słowa kluczowego function, wartość this jest obiekt po lewej stronie kropki wywołanej funkcji
  2. Jeśli z kropki nie pozostaje żaden obiekt, wówczas wartość thiswewnątrz funkcji jest często obiektem globalnym ( globalw węźle, windoww przeglądarce). Nie polecam tutaj używania thissłowa kluczowego, ponieważ jest ono mniej wyraźne niż użycie czegoś podobnegowindow !
  3. Istnieją pewne konstrukcje, takie jak funkcje strzałek i funkcje utworzone za pomocą Function.prototype.bind()funkcji, które mogą naprawić wartośćthis . Są to wyjątki od reguły, ale są naprawdę pomocne w ustaleniu wartości this.

Przykład w węźle JS

module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);

const obj1 = {
    data: "obj1 data",
    met1: function () {
        console.log(this.data);
    },
    met2: () => {
        console.log(this.data);
    },
};

const obj2 = {
    data: "obj2 data",
    test1: function () {
        console.log(this.data);
    },
    test2: function () {
        console.log(this.data);
    }.bind(obj1),
    test3: obj1.met1,
    test4: obj1.met2,
};

obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);

Wynik:

wprowadź opis zdjęcia tutaj

Pozwól mi przeprowadzić Cię przez wyjścia 1 na 1 (ignorując pierwszy dziennik, zaczynając od drugiego):

  1. thisz obj2powodu reguły po lewej stronie kropki, możemy zobaczyć, jak test1się nazywa obj2.test1();.obj2jest kropką, a zatem thiswartością.
  2. Nawet jeśli obj2jest na lewo od kropki, test2jest związany obj1poprzez bind()metody. Więc…this wartość wynosi obj1.
  3. obj2pozostało z kropką z funkcji, które nazywa się: obj2.test3(). Dlatego obj2będzie wartość this.
  4. W tym przypadku: obj2.test4() obj2pozostało kropki. Jednak funkcje strzałek nie mają własnychthis wiązania. Dlatego wiąże się z thiswartością zakresu zewnętrznego, którym jestmodule.exports obiektem, który został zarejestrowany na początku.
  5. Możemy również określić wartość thisza pomocą callfunkcji. Tutaj możemy podać żądaną thiswartość jako argument, co obj2w tym przypadku.
Willem van der Veen
źródło