‘dit’ is niet gedefinieerd in JavaScript-klassemethoden

Ik ben nieuw in JavaScript. Nieuw voor zover ik er echt mee heb gedaan, is het aanpassen van bestaande code en het schrijven van kleine stukjes jQuery.

Nu probeer ik een “klasse” te schrijven met attributen en methoden, maar ik heb problemen met de methoden. Mijn 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

Het probleem is dat in Request.prototype.startthisniet gedefinieerd is en dat de if-instructie als false evalueert. Wat doe ik hier fout?


Antwoord 1, autoriteit 100%

Ik wilde er alleen op wijzen dat deze fout soms optreedt omdat een functie is gebruikt als een functie van hoge orde (doorgegeven als een argument) en dan is de reikwijdte van thisverloren gegaan. In dergelijke gevallen raad ik aan om een ​​dergelijke functie gebonden aan thisdoor te geven. Bijv.

this.myFunction.bind(this);

Antwoord 2, autoriteit 93%

Hoe roep je de startfunctie aan?

Dit zou moeten werken (nieuwis de sleutel)

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

Als je het rechtstreeks aanroept zoals Request.prototype.start(), thisverwijst naar de globale context (windowin browsers) .

Als thisniet gedefinieerd is, resulteert dit ook in een fout. De if-expressie evalueert niet naar false.

Update: thisobject wordt niet ingesteld op basis van declaratie, maar door aanroep. Wat het betekent is dat als je de functie-eigenschap toewijst aan een variabele zoals x = o.starten x()aanroept, thisbinnen start verwijst niet langer naar o. Dit is wat er gebeurt als je setTimeoutdoet. Om het te laten werken, doe je dit in plaats daarvan:

var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);

Antwoord 3, autoriteit 20%

JavaScript’s OOP is een beetje funky (of veel) en het is even wennen. Het eerste waar je rekening mee moet houden is dat er geen lessen zijnen dat denken in termen van lessen je kan laten struikelen. En om een ​​methode te gebruiken die is gekoppeld aan een Constructor (het JavaScript-equivalent van een klassedefinitie), moet u uw object instantiëren. Bijvoorbeeld:

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

Houd er rekening mee dat Ninja-instanties dezelfde eigenschappen hebben, maar aNinjaheeft geen toegang tot de eigenschappen van enemyNinja. (Dit deel zou heel gemakkelijk/eenvoudig moeten zijn) Het wordt een beetje anders als je dingen gaat toevoegen aan het prototype:

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

Als je dit rechtstreeks aanroept, krijg je een foutmelding omdat thisalleen naar het juiste object (je “klasse”) verwijst wanneer de constructor wordt geïnstantieerd (anders verwijst het naar het globale object, windowin een browser)


Antwoord 4, autoriteit 14%

Geen van de vorige antwoorden had de volledige oplossing voor mij, dus plaats de mijne hier.

Ik had een klasse die een fout retourneerde toen ik forEachuitvoerde op de methodereferentie.

bijv.

class Foo {
  hello (name) {
    return `hello ${name}`
  }
  doGreet (name) {
    return console.log(this.hello(name)) // <- 'this' is undefined
  }
}
// print some names...
const foo = new Foo();
(['nick', 'john']).forEach(foo.doGreet)
// TypeError: Cannot read property 'hello' of undefined
//     at doGreet (/.../test.js:7:17)

De oplossing was om de context van thisvan de methode binnen een constructor te binden. d.w.z.

class Foo {
  constructor () {
    this.doGreet = this.doGreet.bind(this)
  }
  // ... yada yada ...
}

Antwoord 5, autoriteit 8%

In ES2015, ook wel ES6 genoemd, is classeen syntactische suiker voor functions.

Als je wilt forceren om een ​​context in te stellen voor thiskun je de bind()methode gebruiken. Zoals @chetan al aangaf, kun je bij aanroep ook de context instellen! Bekijk het onderstaande voorbeeld:

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 hebben we de context binnen handleChange()geforceerd naar Form.


Antwoord 6, autoriteit 7%

Pijlfunctie gebruiken:

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

Antwoord 7, autoriteit 2%

Deze vraag is beantwoord, maar misschien komt hier nog iemand anders.

Ik had ook een probleem waarbij thisniet gedefinieerd is, toen ik dwaas probeerde de methoden van een klasse te destructureren bij het initialiseren ervan:

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

Other episodes