Javascript "dieser" Zeiger innerhalb der verschachtelten Funktion

93

Ich habe eine Frage dazu, wie der Zeiger "this" in einem verschachtelten Funktionsszenario behandelt wird.

Angenommen, ich füge den folgenden Beispielcode in eine Webseite ein. Ich erhalte eine Fehlermeldung, wenn ich die verschachtelte Funktion "doSomeEffects ()" aufrufe. Ich habe Firebug eingecheckt und es zeigt an, dass der Zeiger "this" in dieser verschachtelten Funktion tatsächlich auf das globale "window" -Objekt zeigt - was ich nicht erwartet hatte. Ich muss etwas nicht richtig verstehen, weil ich dachte, da ich die verschachtelte Funktion innerhalb einer Funktion des Objekts deklariert habe, sollte sie in Bezug auf die Funktion einen "lokalen" Bereich haben (dh der "this" -Zeiger würde sich wie auf das Objekt selbst beziehen wie es in meiner ersten "wenn" -Anweisung ist).

Alle Hinweise (kein Wortspiel beabsichtigt) wäre dankbar.

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
quelle
Was genau ist deine Frage?
Sarfraz
2
thisBezieht sich bei Verwendung innerhalb einer Funktion auf das Objekt, für das die Funktion aufgerufen wird.
ca.
8
Was Sie im äußeren Bereich tun könnten, ist so etwas wie var self = this;und dann selfin der inneren Funktion durch Schließen darauf verweisen .
Kai
1
doSomeEffectsist nicht mit einem bestimmten Objekt verbunden, daher thiswird angenommen, dass es das Fenster ist, die Mutter aller Elemente.
ca.
3
@JoJoeDad Wie sage ich das diplomatisch? Aber die Antwort von chuckj unten ist bei weitem die Antwort auf Ihre Frage. Um wirklich zu verstehen, was los ist, sollten Sie den Ausführungskontext , die Bereichskette und dieses Schlüsselwort lesen . Und nach dem Aussehen einiger Antworten hier sollten auch andere Leute sie lesen. Ich versuche, gutes Javascript zu evangelisieren. Deshalb nehme ich mir die Zeit, diese Links zu geben.
onefootswill

Antworten:

120

In JavaScript thisbasiert das Objekt wirklich darauf, wie Sie Ihre Funktionsaufrufe ausführen.

Im Allgemeinen gibt es drei Möglichkeiten, das thisObjekt einzurichten :

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

In allen obigen Beispielen wird das thisObjekt sein someThing. Wenn Sie eine Funktion ohne ein führendes übergeordnetes Objekt aufrufen, erhalten Sie im Allgemeinen das globale Objekt, das in den meisten Browsern das windowObjekt bedeutet .

KylePDavis
quelle
Ich habe den Code this.doSomeEffects();basierend auf Ihrer Antwort geändert , aber es funktioniert immer noch nicht. Warum?
Arashsoft
1
@Arashsoft thisin this.doSomeEffects()Punkten zu std_obj. Wie in der obigen Antwort erläutert, muss eine Funktion, die keine Objektreferenz hat this, ein Fensterobjekt sein.
Shivam
37

Da dies zu den am häufigsten gestellten Fragen dieser Art zu gehören scheint, möchte ich nach all den Jahren die ES6-Lösung mit Pfeilfunktionen hinzufügen:

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") { }
    };
    ...    
  }
};

quelle
5
Nun , dass der Fortschritt!
Joshua Pinter
Hervorragende, einfache Lösung.
Joshua Schlichting
32

thisist nicht Teil des Schließungsbereichs, sondern kann als zusätzlicher Parameter für die Funktion angesehen werden, die an der Aufrufstelle gebunden ist. Wenn die Methode nicht als Methode aufgerufen wird, wird das globale Objekt als übergeben this. Im Browser ist das globale Objekt identisch mit window. Betrachten Sie zum Beispiel die folgende Funktion:

function someFunction() {
}

und das folgende Objekt,

var obj = { someFunction: someFunction };

Wenn Sie die Funktion mit Methodensyntax aufrufen, z.

obj.someFunciton();

dann thisist gebunden an obj.

Wenn Sie someFunction () direkt aufrufen, z.

someFunction();

dann thisist an das globale Objekt gebunden, das heißt window.

Die häufigste Lösung besteht darin, dies in den Verschluss zu erfassen, z.

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
quelle
das = das ist perfekt
Nather Webber
10

Es gibt einen Unterschied zwischen Gehäusevariablen und "dies". "this" wird tatsächlich vom Aufrufer der Funktion definiert, während explizite Variablen innerhalb des als Enclosure bezeichneten Funktionsdeklarationsblocks intakt bleiben. Siehe das folgende Beispiel:

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

Sie können es hier ausprobieren: http://jsfiddle.net/kSTBy/

Was in Ihrer Funktion passiert, ist "doSomeEffects ()", wird explizit aufgerufen, dies bedeutet Kontext oder das "Dies" der Funktion ist das Fenster. Wenn "doSomeEffects" eine Prototypmethode wäre, z. B. this.doSomeEffects bei "myObject", würde myObject.doSomeEffects () dazu führen, dass "this" "myObject" ist.

Shane
quelle
4
Für die Faulen und Ungeduldigen protokolliert diese Demo:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Um diese Frage zu verstehen, versuchen Sie, die Ausgabe für das folgende Snippet abzurufen

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

Der obige Code gibt Folgendes an die Konsole aus:

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

In der äußeren Funktion beziehen sich sowohl dies als auch selbst auf myObject und können daher beide richtig auf foo verweisen und darauf zugreifen.

In der inneren Funktion bezieht sich dies jedoch nicht mehr auf myObject. Infolgedessen ist this.foo in der inneren Funktion undefiniert, während der Verweis auf die lokale Variable self im Gültigkeitsbereich bleibt und dort zugänglich ist. (Vor ECMA 5 würde sich dies in der inneren Funktion auf das globale Fensterobjekt beziehen; während dies in ECMA 5 in der inneren Funktion undefiniert wäre.)

ronakshah725
quelle
1
In der inneren Funktion thisbezieht sich auf das Fensterobjekt (in einer Browser-Umgebung) oder GLOBAL-Objekt (in einer node.js-Umgebung)
Raneshu
2
Warum passiert das?
Setu
@raneshu "use strict" und thisbezieht sich nicht mehr auf window
rofrol
3

Wie von Kyle erklärt, können Sie innerhalb der Funktion Folgendes verwenden calloder applyangeben this:

Hier ist das Konzept, das auf Ihren Code angewendet wird:

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
quelle
0

Ich habe auch die Warnung " Möglicherweise ungültiger Referenzzugriff auf ein Klassenfeld über dieses " erhalten.

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
quelle
0

Da es nicht erwähnt wurde, werde ich erwähnen, dass die Verwendung .bind()eine Lösung ist -


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

    std_obj.displayMe();

Hier ist ein einfacheres Beispiel -

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'

Es ist wahr, dass die Verwendung einer =>Pfeilfunktion schlanker aussieht und für den Programmierer einfach ist. Es sollte jedoch berücksichtigt werden, dass ein lexikalischer Bereich wahrscheinlich mehr Arbeit in Bezug auf Verarbeitung und Speicher erfordert, um diesen lexikalischen Bereich einzurichten und beizubehalten, als einfach eine Funktion thismit einem Zeiger über zu verknüpfen .bind().

Ein Teil des Vorteils der Entwicklung von Klassen in JS bestand darin, eine Methode bereitzustellen, um thiszuverlässiger präsent und verfügbar zu sein, sich von funktionaler Programmierung und lexikalischen Bereichen abzuwenden und so den Overhead zu reduzieren.

Von MDN

Überlegungen zur Leistung Es ist nicht ratsam, unnötigerweise Funktionen innerhalb anderer Funktionen zu erstellen, wenn für eine bestimmte Aufgabe keine Schließungen erforderlich sind, da dies die Skriptleistung sowohl hinsichtlich der Verarbeitungsgeschwindigkeit als auch des Speicherverbrauchs negativ beeinflusst.

Craig Hicks
quelle