'this' ist in JavaScript-Klassenmethoden undefiniert

84

Ich bin neu in JavaScript. Neu, soweit ich es wirklich getan habe, ist der vorhandene Code optimiert und kleine Teile von jQuery geschrieben.

Jetzt versuche ich, eine "Klasse" mit Attributen und Methoden zu schreiben, aber ich habe Probleme mit den Methoden. Mein Code:

function Request(destination, stay_open) {
    this.state = "ready";
    this.xhr = null;
    this.destination = destination;
    this.stay_open = stay_open;

    this.open = function(data) {
        this.xhr = $.ajax({
            url: destination,
            success: this.handle_response,
            error: this.handle_failure,
            timeout: 100000000,
            data: data,
            dataType: 'json',
        });
    };

    /* snip... */

}

Request.prototype.start = function() {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
//all console.log's omitted

Das Problem ist, in Request.prototype.start, thisist nicht definiert und damit die if - Anweisung das Ergebnis falsch. Was mache ich hier falsch?

Carson Myers
quelle
Gibt es einen Grund, den Sie startin der haben prototype?
xj9
Was ist Request.prototypeeingestellt?
Matt Ball
Ich hatte hier eine ähnliche Frage: stackoverflow.com/questions/3198264/…, in der es eine Reihe hilfreicher Links gibt. Der springende Punkt dabei ist, dass thisin JavaScript kein ständiger Verweis auf den 'Eigentümer' einer aufgerufenen prototypischen Funktion vorhanden ist, wie dies in den meisten OO-Sprachen wie Java der Fall wäre.
Marc Bollinger
1
@Matt: Request ist eine Konstruktorfunktion. Der Standardwert für Request.prototype lautet new Object(). Alles, was Sie hinzufügen, wird automatisch zu Eigenschaften von Objekten, die mit erstellt wurden new Request().
Chetan S.
@Matt Ball Request.prototypeist, wo Instanzen von Requesterben. In diesem Fall ist es wahrscheinlich Functionoder Object.
xj9

Antworten:

68

Wie ruft man die Startfunktion auf?

Dies sollte funktionieren ( neu ist der Schlüssel)

var o = new Request(destination, stay_open);
o.start();

Wenn Sie es direkt so nennen Request.prototype.start(), thiswird auf den globalen Kontext ( windowin Browsern) verwiesen.

Wenn dies nicht thisdefiniert ist, führt dies zu einem Fehler. Der if-Ausdruck wird nicht als false ausgewertet.

Update : thisObjekt wird nicht basierend auf der Deklaration festgelegt, sondern durch Aufruf . Wenn Sie die Funktionseigenschaft einer Variablen wie x = o.startund aufrufen x(), thisbezieht sich dies nicht mehr auf inside start o. Dies ist, was passiert, wenn Sie dies tun setTimeout. Gehen Sie stattdessen folgendermaßen vor, damit es funktioniert:

 var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);
Chetan S.
quelle
Ich benutze setTimeout:var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
Carson Myers
Dies rettete mir das Leben, als ich versuchte zu verstehen, warum die Funktion, die ich zum Ausdrücken von ' basicAuth ' übergab, nicht mit derselben Ausgabe funktionierte.
Edison Spencer
Oder tun o.start.bind(o). Warum funktioniert das nicht x = o.start; x()?
theonlygusti
.bind()Gibt eine neue Funktion zurück, die nicht direkt funktioniert. Sie x = o.start.bind(o); x()o.start = o.start.bind(o); x=o.start; x()
müssten also
38

Ich wollte nur darauf hinweisen, dass dieser Fehler manchmal auftritt, weil eine Funktion als Funktion höherer Ordnung verwendet wurde (als Argument übergeben) und dann der Umfang von thisverloren gegangen ist. In solchen Fällen würde ich empfehlen, eine solche Funktion gebunden an zu übergeben this. Z.B

this.myFunction.bind(this);
EliuX
quelle
3
Gute Erklärung !! Genau das habe ich gesucht.
Vandersondf
1
Es ist nicht abzusehen, wie viel Kopfschmerzen Sie mir gerade erspart haben
Native Coder
Warum geht der Umfang von thisverloren?
theonlygusti
Hat mir viel Zeit gespart. Vielen Dank!
Oder Assayag
17

JavaScript's OOP ist ein bisschen funky (oder viel) und es ist gewöhnungsbedürftig. Das erste, was Sie beachten müssen, ist das es keine Klassen gibt und das Denken in Klassen Sie stolpern kann. Um eine an einen Konstruktor angehängte Methode zu verwenden (das JavaScript-Äquivalent einer Klassendefinition), müssen Sie Ihr Objekt instanziieren. Zum Beispiel:

Ninja = function (name) {
    this.name = name;
};
aNinja = new Ninja('foxy');
aNinja.name; //-> 'foxy'

enemyNinja = new Ninja('boggis');
enemyNinja.name; //=> 'boggis'

Beachten Sie, dass NinjaInstanzen dieselben Eigenschaften haben, jedoch aNinjanicht auf die Eigenschaften von zugreifen könnenenemyNinja . (Dieser Teil sollte wirklich einfach / unkompliziert sein.) Die Dinge werden etwas anders, wenn Sie anfangen, Dinge zu folgenden Elementen hinzuzufügen prototype:

Ninja.prototype.jump = function () {
   return this.name + ' jumped!';
};
Ninja.prototype.jump(); //-> Error.
aNinja.jump(); //-> 'foxy jumped!'
enemyNinja.jump(); //-> 'boggis jumped!'

Wenn Sie dies direkt aufrufen, wird ein Fehler ausgegeben, da thisnur dann auf das richtige Objekt (Ihre "Klasse") verwiesen wird, wenn der Konstruktor instanziiert wird (andernfalls zeigt er windowin einem Browser auf das globale Objekt ).

xj9
quelle
6

In ES2015 aka ES6 classist ein syntaktischer Zucker fürfunctions .

Wenn Sie das Festlegen eines Kontexts erzwingen möchten, thiskönnen Sie die bind()Methode verwenden. Wie @chetan betonte, können Sie beim Aufruf auch den Kontext festlegen! Überprüfen Sie das folgende Beispiel:

class Form extends React.Component {
constructor() {
    super();
  }
  handleChange(e) {
    switch (e.target.id) {
      case 'owner':
        this.setState({owner: e.target.value});
        break;
      default:
    }
  }
  render() {
    return (
      <form onSubmit={this.handleNewCodeBlock}>
        <p>Owner:</p> <input onChange={this.handleChange.bind(this)} />
      </form>
    );
  }
}

Hier wir gezwungen , den Kontext innen handleChange()zu Form.

Nitin
quelle
6
Sie sollten die Funktion im Konstruktor daran binden. Andernfalls binden Sie es jedes Mal, renderwenn es aufgerufen wird, anstatt einmal, wenn die Klasse instanziiert wird. Außerdem ist der größte Teil Ihres Beispiels für die Frage nicht wirklich relevant.
erich2k8
Oder verwenden Sie die Pfeilsyntax, wenn Sie denhandleChange()
Nitin
0

Diese Frage wurde beantwortet, aber vielleicht kommt jemand anderes hierher.

Ich hatte auch ein Problem, bei dem thises nicht definiert ist, als ich dummerweise versuchte, die Methoden einer Klasse bei der Initialisierung zu zerstören:

import MyClass from "./myClass"

// 'this' is not defined here:
const { aMethod } = new MyClass()
aMethod() // error: 'this' is not defined

// So instead, init as you would normally:
const myClass = new MyClass()
myClass.aMethod() // OK

Oli
quelle
0

Verwenden Sie die Pfeilfunktion:

Request.prototype.start = () => {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
Kasra
quelle