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
, this
jest niezdefiniowana, a zatem jeśli analizuje schemat rachunku false. Co ja tu robię źle?
javascript
class
prototype
Carson Myers
źródło
źródło
start
wprototype
?Request.prototype
ustawione?this
w 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.new Object()
. Wszystko, co do niej dodasz, automatycznie staje się właściwościami obiektów utworzonych za pomocąnew Request()
.Request.prototype
jest miejscemRequest
dziedziczenia z. W tym przypadku jest to prawdopodobnieFunction
lubObject
.Odpowiedzi:
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()
,this
będzie odnosić się do kontekstu globalnego (window
w przeglądarkach).Ponadto, jeśli
this
jest niezdefiniowane, powoduje błąd. Wyrażenie if nie daje wartości false.Aktualizacja :
this
obiekt nie jest ustawiany na podstawie deklaracji, ale przez wywołanie . Oznacza to, że jeśli przypiszesz właściwość funkcji do zmiennej takiej jakx = o.start
and callx()
,this
inside start już się do niej nie odnosio
. Tak się dzieje, gdy to robiszsetTimeout
. Aby to zadziałało, zrób to:var o = new Request(...); setTimeout(function() { o.start(); }, 1000);
źródło
var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
o.start.bind(o)
. Dlaczego niex = o.start; x()
działa?.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()
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
this
został utracony. W takich przypadkach zalecałbym przekazanie takiej funkcji związanejthis
. Na przykładthis.myFunction.bind(this);
źródło
this
gubi się zakres ?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
Ninja
wystąpienia mają te same właściwości, aleaNinja
nie mogą uzyskać dostępu do właściwościenemyNinja
. (Ta część powinna być naprawdę łatwa / nieskomplikowana) Sytuacja wygląda nieco inaczej, gdy zaczniesz dodawać rzeczy doprototype
: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ż
this
wskazuje tylko na właściwy obiekt (twoją „klasę”), gdy tworzony jest instancja Konstruktora (w przeciwnym razie wskazuje na obiekt globalnywindow
w przeglądarce)źródło
W ES2015 aka ES6
class
jest 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()
doForm
.źródło
render
zostanie wywołana, a nie raz, gdy tworzona jest instancja klasy. Ponadto większość twojego przykładu nie jest tak naprawdę odpowiednia dla pytania.handleChange()
Odpowiedź na to pytanie została udzielona, ale może ktoś inny przyjedzie tutaj.
Miałem również problem z
this
niezdefiniowanym, 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
źródło
Użyj funkcji strzałki:
Request.prototype.start = () => { if( this.stay_open == true ) { this.open({msg: 'listen'}); } else { } };
źródło