Wskaźnik JavaScript „this” w funkcji zagnieżdżonej

94

Mam pytanie dotyczące sposobu traktowania wskaźnika „this” w scenariuszu funkcji zagnieżdżonej.

Powiedzmy, że wstawiam następujący przykładowy kod na stronę internetową. Gdy wywołuję zagnieżdżoną funkcję „doSomeEffects ()”, pojawia się błąd. Sprawdziłem w Firebug i wskazuje, że kiedy jestem w tej zagnieżdżonej funkcji, wskaźnik „this” w rzeczywistości wskazuje na globalny obiekt „window” - czego się nie spodziewałem. Nie mogę rozumieć czegoś poprawnie, ponieważ pomyślałem, że skoro zadeklarowałem zagnieżdżoną funkcję w funkcji obiektu, powinna mieć zasięg „lokalny” w stosunku do funkcji (tj. Wskaźnik „ten” odnosiłby się do samego obiektu jak jak to jest w moim pierwszym stwierdzeniu „jeśli”).

Wszelkie wskazówki (gra słów nie przeznaczona) będą mile widziane.

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();
JoJoeDad
źródło
Jakie jest Twoje pytanie?
Sarfraz,
2
Używane wewnątrz funkcji thisodnosi się do obiektu, dla którego funkcja jest wywoływana.
ok.
9
To, co możesz zrobić w zakresie zewnętrznym, jest czymś w rodzaju, var self = this;a następnie odnosić się do selffunkcji wewnętrznej poprzez zamknięcie.
Kai
1
doSomeEffectsnie jest powiązany z żadnym konkretnym obiektem, więc thiszakłada się, że jest oknem, matką wszystkich elementów.
ok.
3
@JoJoeDad Jak dyplomatycznie to powiedzieć? Ale odpowiedź udzielona przez Chuckj poniżej jest zdecydowanie odpowiedzią na twoje pytanie. Aby naprawdę zrozumieć, co się dzieje, powinieneś przeczytać kontekst wykonania , łańcuch zasięgu i to słowo kluczowe . Wygląda na to, że niektóre odpowiedzi są tutaj, że inni też powinni je przeczytać. Staram się ewangelizować dobry javascript. Dlatego poświęcam trochę czasu, aby podać te linki.
onefoots będzie

Odpowiedzi:

121

W JavaScript thisobiekt jest tak naprawdę oparty na sposobie wywoływania funkcji.

Ogólnie rzecz biorąc, istnieją trzy sposoby konfiguracji thisobiektu:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

We wszystkich powyższych przykładach thisobiektem będzie someThing. Wywołanie funkcji bez wiodącego obiektu nadrzędnego generalnie da ci obiekt globalny, który w większości przeglądarek oznacza windowobiekt.

KylePDavis
źródło
Zmieniłem kod na this.doSomeEffects();oparty na Twojej odpowiedzi, ale nadal nie działa. Czemu?
Arashsoft
1
@Arashsoft thisw this.doSomeEffects()wskazuje na std_obj. Jak wyjaśniono w powyższej odpowiedzi, jeśli funkcja nie ma odniesienia do obiektu, thismusi być obiektem okna.
Shivam
42

Ponieważ wydaje się, że jest to jedno z najbardziej opiniotwórczych pytań tego rodzaju, pozwolę sobie dodać po tych wszystkich latach rozwiązanie ES6 wykorzystujące funkcje strzałkowe:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

źródło
6
Teraz to postęp!
Joshua Pinter,
Doskonałe, proste rozwiązanie.
Joshua Schlichting
Jak mogłem nie wiedzieć przez ostatnie 4 lata, że ​​to jest coś.
xorinzor
32

thisnie jest częścią zakresu zamknięcia, może być traktowany jako dodatkowy parametr funkcji, która jest powiązana w miejscu wywołania. Jeśli metoda nie zostanie wywołana jako metoda, obiekt globalny jest przekazywany jako this. W przeglądarce obiekt globalny jest identyczny z window. Weźmy na przykład pod uwagę następującą funkcję,

function someFunction() {
}

i następujący przedmiot,

var obj = { someFunction: someFunction };

Jeśli wywołasz funkcję przy użyciu składni metody, takiej jak,

obj.someFunciton();

wtedy thisjest zobowiązany obj.

Jeśli wywołasz funkcję someFunction () bezpośrednio, na przykład

someFunction();

to thisznaczy jest powiązany z obiektem globalnym window.

Najczęstszym sposobem obejścia tego problemu jest uwzględnienie tego w zamknięciu, na przykład

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      
chuckj
źródło
that = this is perfect
Nather Webber
9

Istnieje różnica między zmiennymi obejmującymi a „to”. „this” jest w rzeczywistości definiowane przez wywołującego funkcję, podczas gdy jawne zmienne pozostają nienaruszone wewnątrz bloku deklaracji funkcji, zwanego obudową. Zobacz poniższy przykład:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

możesz to wypróbować tutaj: http://jsfiddle.net/kSTBy/

To, co dzieje się w Twojej funkcji, to „doSomeEffects ()”, jest wywoływane jawnie, co oznacza, że ​​kontekst lub „this” funkcji to okno. jeśli „doSomeEffects” było metodą prototypową, np. this.doSomeEffects na powiedz „myObject”, to myObject.doSomeEffects () spowoduje, że „this” będzie „myObject”.

Shane
źródło
4
dla leniwych i niecierpliwych to demo rejestruje:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Aby zrozumieć to pytanie, spróbuj uzyskać dane wyjściowe dla następującego fragmentu kodu

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();

Powyższy kod wyświetli na konsoli następujący kod:

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

W zewnętrznej funkcji zarówno this, jak i self odnoszą się do myObject i dlatego oba mogą poprawnie odwoływać się do foo i uzyskiwać do niego dostęp.

Jednak w funkcji wewnętrznej nie odnosi się to już do myObject. W rezultacie this.foo jest niezdefiniowane w funkcji wewnętrznej, podczas gdy odwołanie do zmiennej lokalnej self pozostaje w zakresie i jest tam dostępne. (Przed ECMA 5, to w funkcji wewnętrznej odnosiłoby się do globalnego obiektu okna; podczas gdy od ECMA 5, w funkcji wewnętrznej byłoby niezdefiniowane).

ronakshah725
źródło
1
W funkcji wewnętrznej thisodnosi się do obiektu okna (w środowisku przeglądarki) lub obiektu GLOBAL (w środowisku node.js)
raneshu
2
Dlaczego tak się dzieje?
setu
@raneshu „użyj ścisłego” i thisnie odnosi się już do okna
rofrol
3

Jak wyjaśnił Kyle, możesz użyć calllub applydo określenia thisw funkcji:

Oto koncepcja zastosowana do twojego kodu:

var std_obj = {
    options: {
        rows: 0,
        cols: 0
    },
    activeEffect: "none",
    displayMe: function() {

        // the 'this' pointer is referring to the std_obj
        if (this.activeEffect == "fade") {}

        var doSomeEffects = function() {
            // the 'this' pointer is referring to the window obj, why?
            if (this.activeEffect == "fade") {}
        }

        doSomeEffects.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle

Hari Pachuveetil
źródło
0

Otrzymałem również ostrzeżenie „ Potencjalnie nieprawidłowy dostęp do odwołania do pola klasy za pośrednictwem tego

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class
CodeToLife
źródło
0

Ponieważ nie zostało to wspomniane, wspomnę, że używanie .bind()jest rozwiązaniem -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

Oto prostszy przykład -

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

Prawdą jest, że używanie funkcji strzałek =>wygląda na bardziej przejrzyste i łatwe dla programisty. Należy jednak pamiętać, że zakres leksykalny będzie prawdopodobnie wymagał więcej pracy pod względem przetwarzania i pamięci, aby ustawić i utrzymać ten zakres leksykalny, w porównaniu do zwykłego powiązania funkcji thisze wskaźnikiem za pomocą.bind() .

Częścią korzyści z tworzenia klas w JS było zapewnienie metody do wykonania this bardziej niezawodnej prezentacji i dostępności, aby odejść od programowania funkcjonalnego i zakresów leksykalnych, a tym samym zmniejszyć narzut.

Z MDN

Rozważania dotyczące wydajności Niepotrzebne jest tworzenie funkcji w ramach innych funkcji, jeśli zamknięcia nie są potrzebne do określonego zadania, ponieważ wpłynie to negatywnie na wydajność skryptu, zarówno pod względem szybkości przetwarzania, jak i zużycia pamięci.

Craig Hicks
źródło