it-swarm.com.de

Javascript Closures und 'this'

Ich habe ein Problem mit einem von mir erstellten Objekt, das ungefähr so ​​aussieht:

var myObject = {

    AddChildRowEvents: function(row, p2) {
        if(document.attachEvent) {
            row.attachEvent('onclick', function(){this.DoSomething();});
        } else {
            row.addEventListener('click', function(){this.DoSomething();}, false);
        }
    },

    DoSomething: function() {
        this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
    }
}

Das Problem ist, dass wenn ich mich in der 'DoSomething'-Funktion befinde, sich' dies 'nicht auf' myObject 'bezieht, was mache ich falsch?

34
Chris

Wenn die Funktion aufgerufen wird, bezieht sich "this" auf row. Wenn du das Objekt haben willst, kannst du es in etwa so machen:]

AddChildRowEvents: function(row, p2) {
    var theObj = this;
    if(document.attachEvent) {
         row.attachEvent('onclick', function(){theObj.DoSomething();});
    } else {
         row.addEventListener('click', function(){theObj.DoSomething();}, false);
    }
},

Wenn die Funktion aufgerufen wird, hat sie Zugriff auf die Variable theOBj, die zum Zeitpunkt der Definition der Funktion im Gültigkeitsbereich war.

34
Mike Kantor

this bezieht sich immer auf die innere Funktion. Wenn Sie verschachtelte Funktionen haben, müssen Sie eine andere Variable erstellen und diese auf this verweisen.

var myObject = {
    AddChildRowEvents: function(row, p2) {
        var that = this;
        if(document.attachEvent) {
            row.attachEvent('onclick', function(){that.DoSomething();});
        } else {
            row.addEventListener('click', function(){that.DoSomething();}, false);
        }
    }
}
8
svinto

Dies ist ein häufiges Problem bei Schließungen. Versuchen Sie Folgendes, um das Problem zu beheben:

var myObject = {    
    AddChildRowEvents: function(row, p2) { 
        var self = this;

        if(document.attachEvent) {            
             row.attachEvent('onclick', function(){this.DoSomething(self);});        
        } else {            
             row.addEventListener('click', function(){this.DoSomething(self);}, false);        
        }    
    },    

    DoSomething: function(self) {       
        self.SomethingElse(); 
    }
}
3
Chris Schick

Das Problem mit Ihrem Code besteht darin, dass Sie in der folgenden Codezeile eine anonyme Funktion definiert und diese in den folgenden Zeilen als Ereignishandler für das onClick-Ereignis übergeben haben:

    row.attachEvent('onclick', function(){this.DoSomething();});

und

    row.addEventListener('click', function(){this.DoSomething();}, false);

wenn ein Klickereignis ausgelöst wird und die von Ihnen übergebene anonyme Funktion aufgerufen wird, bezieht sich this context auf das Objekt, das das Ereignis ausgelöst hat, nicht jedoch auf myObject . da sich das Ausführen des Kontexts zu diesem Zeitpunkt im Event-Handler-Kontext befindet.

Die Lösung: Sie können den Trick ausführen, der Mike Kantor mitgeteilt wurde, oder mithilfe von jQuery die Proxy-Methode verwenden, um this context in der anonymen Funktion zu definieren.

Ihr Code würde also so aussehen:

var myObject = {

    AddChildRowEvents: function(row, p2) {
        if(document.attachEvent) {
            row.attachEvent('onclick', $.proxy(function () {
                this.DoSomething();
            }, this));
        } else {
            row.addEventListener('click', $.proxy(function () {
                this.DoSomething();
            },this), false);
        }
    },

    DoSomething: function() {
        this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
    }
}

sie haben erwähnt, dass sich der Fehler in this.SomthingElse () befindet, Ihr Code zeigt ihn jedoch nicht an. Wenn in dieser Codezeile wirklich ein Fehler auftritt, verwenden Sie möglicherweise die Methode DoSomthing als Ereignishandler.

2
Farshid Saberi

Das Problem liegt darin, wie this in JS verwaltet wird. Dies kann schnell geschehen, insbesondere, wenn Sie einen asynchronen Rückruf haben (und wenn ein an diesen gebundener Abschluss erstellt wird).

Wenn die AddChildRowEvents-Methode aufgerufen wird, verweist die this gut auf die Variable myObject. Ziel dieser Methode ist es jedoch, einen Handler für das Klicken bereitzustellen. Das Ereignis wird also irgendwann in der Zukunft ausgelöst. Zu diesem Zeitpunkt löst welches Objekt das Ereignis wirklich aus? Tatsächlich ist es der DOM element, auf den der Benutzer klickt. this verweist also auf diesen DOM element und nicht auf die Variable myObject.

Für eine modernere Lösung als die vorgestellten kann man bind method oder arrow function verwenden.

1. Lösung mit Pfeilfunktion

let handler = {
        addEventHandler: function(row) {
          console.log("this", this)
        row.addEventListener("click", () => {
        console.log("this", this)
        this.doSomethingElse()
    })
  },
  
  doSomethingElse: function() {
        console.log("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

Mit arrow function (die seit ES2015 verfügbar sind) wird der Kontext this nicht im Ausführungskontext, sondern im lexikalischen Kontext angezeigt. Der Unterschied zwischen beiden besteht darin, dass der lexikalische Kontext zur Erstellungszeit und nicht zur Ausführungszeit gefunden wird, sodass der lexikalische Kontext leichter zu verfolgen ist. Hier referenziert innerhalb des arrow function die this dieselbe this der äußeren Funktion (d. H. addEventHandler), also referenzieren Sie myObject.

Weitere Erläuterungen zu den Eigenschaften der Fettpfeilfunktion im Vergleich zu regulären Funktionen: https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/

2. Lösung mit Binden

let handler = {
        addEventHandler: function(row) {
          console.log("this", this)
        row.addEventListener("click", function() {
        console.log("this", this)
        this.doSomethingElse()
    }.bind(this))
  },
  
  doSomethingElse: function() {
        console.log("something else")
  }
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>

Dieses Mal, wenn die Rückruffunktion an addEventListener übergeben wird, hat man den Kontext this an das Element this in der externen Funktion addEventHandler gebunden.

0
pom421