Wartość „this” jest niezdefiniowana w metodach klas JavaScript

84

Jestem nowy w JavaScript. Nowością, co naprawdę z nim zrobiłem, jest poprawienie istniejącego kodu i napisanie małych fragmentów jQuery.

Teraz próbuję napisać „klasę” z atrybutami i metodami, ale mam problem z metodami. Mój kod:

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

Problemem jest to, w Request.prototype.start, thisjest niezdefiniowana, a zatem jeśli analizuje schemat rachunku false. Co ja tu robię źle?

Carson Myers
źródło
Czy jest jakiś powód, który masz startw prototype?
xj9
Co jest Request.prototypeustawione?
Matt Ball,
Miałem podobne pytanie tutaj: stackoverflow.com/questions/3198264/… w którym jest kilka pomocnych linków. Sedno tego polega na tym, że thisw JavaScript nie występuje ciągłe odniesienie do „właściciela” wywoływanej funkcji prototypowej, jak ma to miejsce w większości języków obiektowych, takich jak Java.
Marc Bollinger
1
@Matt: Request jest funkcją konstruktora. Request.prototype domyślnie new Object(). Wszystko, co do niej dodasz, automatycznie staje się właściwościami obiektów utworzonych za pomocą new Request().
Chetan S,
@Matt Ball Request.prototypejest miejscem Requestdziedziczenia z. W tym przypadku jest to prawdopodobnie Functionlub Object.
xj9

Odpowiedzi:

68

Jak wywołujesz funkcję start?

To powinno działać ( nowość jest kluczem)

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

Jeśli bezpośrednio nazwiesz to jak Request.prototype.start(), thisbędzie odnosić się do kontekstu globalnego ( windoww przeglądarkach).

Ponadto, jeśli thisjest niezdefiniowane, powoduje błąd. Wyrażenie if nie daje wartości false.

Aktualizacja : thisobiekt nie jest ustawiany na podstawie deklaracji, ale przez wywołanie . Oznacza to, że jeśli przypiszesz właściwość funkcji do zmiennej takiej jak x = o.startand call x(), thisinside start już się do niej nie odnosi o. Tak się dzieje, gdy to robisz setTimeout. Aby to zadziałało, zrób to:

 var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);
Chetan S
źródło
Używam setTimeout:var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
Carson Myers,
To uratowało mi życie, gdy próbowałem zrozumieć, dlaczego funkcja, której użyłem, aby wyrazić „ basicAuth ”, nie działa z tym samym wyjściem.
Edison Spencer
Albo zrób o.start.bind(o). Dlaczego nie x = o.start; x()działa?
theonlygusti
.bind()zwraca nową funkcję, która nie działa w miejscu. Więc musiałbyś zrobić x = o.start.bind(o); x()lubo.start = o.start.bind(o); x=o.start; x()
ceedob
38

Chciałem tylko zaznaczyć, że czasami ten błąd występuje, ponieważ funkcja została użyta jako funkcja wyższego rzędu (przekazana jako argument), a następnie zakres thiszostał utracony. W takich przypadkach zalecałbym przekazanie takiej funkcji związanej this. Na przykład

this.myFunction.bind(this);
EliuX
źródło
3
Dobre wytłumaczenie !! To było dokładnie to, czego szukałem.
vandersondf
1
nie wiadomo, ile bólu głowy właśnie uratowałeś
Native Coder
Dlaczego thisgubi się zakres ?
theonlygusti
17

JavaScript OOP jest trochę funky (lub dużo) i trzeba się do niego przyzwyczaić. Pierwszą rzeczą, o której musisz pamiętać, jest to, że nie ma żadnych klas, a myślenie w kategoriach zajęć może cię potknąć. Aby użyć metody dołączonej do Konstruktora (odpowiednik definicji klasy w języku JavaScript), musisz utworzyć instancję obiektu. Na przykład:

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

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

Należy zauważyć, że Ninjawystąpienia mają te same właściwości, ale aNinjanie mogą uzyskać dostępu do właściwości enemyNinja. (Ta część powinna być naprawdę łatwa / nieskomplikowana) Sytuacja wygląda nieco inaczej, gdy zaczniesz dodawać rzeczy do prototype:

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

Bezpośrednie wywołanie tego wywoła błąd, ponieważ thiswskazuje tylko na właściwy obiekt (twoją „klasę”), gdy tworzony jest instancja Konstruktora (w przeciwnym razie wskazuje na obiekt globalny windoww przeglądarce)

xj9
źródło
6

W ES2015 aka ES6 classjest cukrem syntaktycznym dlafunctions .

Jeśli chcesz wymusić ustawienie kontekstu this, możesz użyć bind()metody. Jak zauważył @chetan, przy wywołaniu możesz również ustawić kontekst! Sprawdź poniższy przykład:

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

Tu zmuszony kontekst wewnątrz handleChange()do Form.

Nitin
źródło
6
Powinieneś powiązać funkcję z this w konstruktorze. W przeciwnym razie będziesz ją wiązać za każdym razem, gdy renderzostanie wywołana, a nie raz, gdy tworzona jest instancja klasy. Ponadto większość twojego przykładu nie jest tak naprawdę odpowiednia dla pytania.
erich2k8
Lub użyj składni strzałek podczas definiowaniahandleChange()
Nitin
0

Odpowiedź na to pytanie została udzielona, ​​ale może ktoś inny przyjedzie tutaj.

Miałem również problem z thisniezdefiniowanym, gdy głupio próbowałem zniszczyć metody klasy podczas jej inicjalizacji:

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
źródło
0

Użyj funkcji strzałki:

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

    }
};
Kasra
źródło