'esto' no está definido en los métodos de clase de JavaScript

83

Soy nuevo en JavaScript. Lo nuevo en cuanto a lo que realmente he hecho con él es modificar el código existente y escribir pequeños fragmentos de jQuery.

Ahora estoy intentando escribir una "clase" con atributos y métodos, pero tengo problemas con los métodos. Mi código:

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

El problema es que, en Request.prototype.start, thisno está definido y, por lo tanto, la instrucción if se evalúa como falsa. ¿Qué estoy haciendo mal aquí?

Carson Myers
fuente
¿Hay alguna razón que tenga starten el prototype?
xj9
¿Qué está Request.prototypeconfigurado para?
Matt Ball
Tenía una pregunta similar aquí: stackoverflow.com/questions/3198264/… en la que hay un montón de enlaces útiles. El quid de esto es que thisen JavaScript no es una referencia constante al 'propietario' de una función prototípica que se llama, como sería en la mayoría de lenguajes OO como Java.
Marc Bollinger
1
@Matt: Request es una función de constructor. Request.prototype tiene el valor predeterminado new Object(). Todo lo que le agregue se convierte automáticamente en propiedades de los objetos creados con new Request().
Chetan S
@Matt Ball Request.prototypees de donde Requestheredan las instancias . En este caso probablemente sea Functiono Object.
xj9

Respuestas:

68

¿Cómo llamas a la función de inicio?

Esto debería funcionar (lo nuevo es la clave)

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

Si lo llama directamente como Request.prototype.start(), thisse referirá al contexto global ( windowen los navegadores).

Además, si thisno está definido, da como resultado un error. La expresión if no se evalúa como falsa.

Actualización : el thisobjeto no se establece en función de la declaración, sino por invocación . Lo que significa es que si asigna la propiedad de la función a una variable como x = o.starty llama x(), el thisinicio interno ya no se refiere a o. Esto es lo que pasa cuando lo haces setTimeout. Para que funcione, haz esto en su lugar:

 var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);
Chetan S
fuente
Estoy usando setTimeout:var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
Carson Myers
Esto me salvó la vida, al tratar de entender por qué la función que estaba pasando para expresar ' basicAuth no funcionaba con la misma salida.
Edison Spencer
O hazlo o.start.bind(o). ¿Por qué no x = o.start; x()funciona?
theonlygusti
36

Solo quería señalar que a veces este error ocurre porque una función se ha utilizado como una función de orden superior (pasada como argumento) y luego thisse perdió el alcance de . En tales casos, recomendaría pasar dicha función vinculada a this. P.ej

this.myFunction.bind(this);
EliuX
fuente
3
Buena explicación !! Eso era exactamente lo que estaba buscando.
vandersondf
1
no se sabe cuánto dolor de cabeza me acaba de salvar
Native Coder
¿Por qué thisse pierde el alcance de ?
theonlygusti
17

La programación orientada a objetos de JavaScript es un poco extravagante (o mucho) y lleva un tiempo acostumbrarse. Lo primero que debes tener en cuenta es que no hay Clases y pensar en términos de clases puede hacerte tropezar. Y para utilizar un método adjunto a un Constructor (el equivalente en JavaScript de una definición de clase), necesita crear una instancia de su objeto. Por ejemplo:

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

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

Tenga en cuenta que las Ninjainstancias tienen las mismas propiedades pero aNinjano pueden acceder a las propiedades de enemyNinja. (Esta parte debería ser realmente fácil / directa) Las cosas se ponen un poco diferentes cuando comienzas a agregar cosas a prototype:

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

Llamar a esto directamente arrojará un error porque thissolo apunta al objeto correcto (su "Clase") cuando se crea una instancia del Constructor (de lo contrario, apunta al objeto global, windowen un navegador)

xj9
fuente
6

En ES2015, también conocido como ES6, classes un azúcar sintáctico para functions.

Si desea forzar el establecimiento de un contexto this, puede usar el bind()método. Como señaló @chetan, ¡en la invocación también puede establecer el contexto! Mira el ejemplo a continuación:

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

Aquí forzamos el contexto handleChange()hacia adentro Form.

Nitin
fuente
6
Debes vincular la función a esto en el constructor. De lo contrario, lo vincula cada vez que renderse llama en lugar de una vez cuando se crea una instancia de la clase. Además, la mayor parte de su ejemplo no es realmente relevante para la pregunta.
erich2k8
O use la sintaxis de flecha al definir elhandleChange()
Nitin
0

Esta pregunta ha sido respondida, pero tal vez alguien más venga aquí.

También tuve un problema donde thisno está definido, cuando estaba tratando tontamente de desestructurar los métodos de una clase al inicializarla:

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
fuente