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?


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

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

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)


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.


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


0

Użyj funkcji strzałki:

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

    }
};
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.