web-dev-qa-db-fra.com

'ceci' n'est pas défini dans les méthodes de classe JavaScript

Je suis nouveau sur JavaScript. Nouveau dans la mesure où tout ce que j'ai vraiment fait est de modifier le code existant et d'écrire de petits morceaux de jQuery.

Maintenant, j'essaie d'écrire une "classe" avec des attributs et des méthodes, mais j'ai des problèmes avec les méthodes. Mon 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

Le problème est, dans Request.prototype.start, this n'est pas défini et donc l'instruction if est évaluée à false. Qu'est-ce que je fais mal ici?

61
Carson Myers

Comment appelez-vous la fonction de démarrage?

Cela devrait fonctionner (nouvea est la clé)

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

Si vous l'appelez directement comme Request.prototype.start(), this fera référence au contexte global (window dans les navigateurs).

De plus, si this n'est pas défini, il en résulte une erreur. L'expression if n'a pas la valeur false.

Mettre à jour: this l'objet n'est pas défini en fonction de la déclaration, mais par invocation . Cela signifie que si vous affectez la propriété de fonction à une variable comme x = o.start Et appelez x(), this à l'intérieur de start ne fait plus référence à o. C'est ce qui se produit lorsque vous effectuez setTimeout. Pour le faire fonctionner, faites-le à la place:

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

JavaScript OOP est un peu génial (ou beaucoup) et il faut un certain temps pour s'y habituer. Cette première chose que vous devez garder à l'esprit est que il n'y a pas de classes et penser en termes de classes peut vous tromper. Et pour utiliser une méthode attachée à un constructeur (l'équivalent JavaScript d'une définition de classe), vous devez instancier votre objet. Par exemple:

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

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

Notez que les instances Ninja ont les mêmes propriétés mais aNinja ne peut pas accéder aux propriétés de enemyNinja. (Cette partie devrait être vraiment facile/directe) Les choses deviennent un peu différentes lorsque vous commencez à ajouter des éléments à prototype:

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

L'appel direct génère une erreur car this pointe uniquement vers l'objet correct (votre "classe") lorsque le constructeur est instancié (sinon il pointe vers l'objet global, window dans un navigateur)

15
xj9

Dans ES2015 alias ES6, class est un sucre syntaxique pour functions.

Si vous souhaitez forcer la définition d'un contexte pour this, vous pouvez utiliser la méthode bind(). Comme l'a souligné @chetan, lors de l'invocation, vous pouvez également définir le contexte! Consultez l'exemple ci-dessous:

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

Ici, nous avons forcé le contexte à l'intérieur de handleChange() à Form.

4
Nitin