var self = ceci?

182

En utilisant des méthodes d'instance comme pour les callbacks gestionnaires d'événements change la portée de thisde « Mon exemple » à « Tout ce que vient d' appeler la fonction de rappel » . Donc mon code ressemble à ceci

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Cela fonctionne, mais est-ce la meilleure façon de procéder? Cela me semble étrange.

defnull
la source
15
Vous devriez éviter selfcar il y a un window.selfobjet et vous pourriez finir par l'utiliser accidentellement si vous oubliez de déclarer votre propre selfvar (par exemple lorsque vous déplacez du code). Cela peut être ennuyeux à repérer / déboguer. Mieux vaut utiliser quelque chose comme _this.
Alastair Maw
3
Extrait du didacticiel JavaScript : "La valeur de thisest dynamique en JavaScript. Elle est déterminée lorsque la fonction est appelée , pas lorsqu'elle est déclarée."
DavidRR
La chose est, dans la portée mondiale self === this. Par conséquent, selfdans des contextes locaux, cela a du sens et suit le modèle.
programmer5000

Réponses:

210

Cette question n'est pas spécifique à jQuery, mais spécifique à JavaScript en général. Le problème central est de savoir comment «canaliser» une variable dans les fonctions intégrées. Voici l'exemple:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Cette technique repose sur l'utilisation d'une fermeture. Mais cela ne fonctionne pas thiscar il thiss'agit d'une pseudo variable qui peut changer dynamiquement d'une portée à l'autre:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

Que pouvons-nous faire? Assignez-le à une variable et utilisez-le via l'alias:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisn'est pas unique à cet égard: argumentsest l'autre pseudo variable qui devrait être traitée de la même manière - par aliasing.

Eugène Lazutkin
la source
7
Vous devriez vraiment lier "this" à l'appel de fonction et ne pas le passer ou traverser des portées variables avec lui
michael
10
@michael Pourquoi ça?
Chris Anderson
@michael, vous travaillez peut-être à l'intérieur du tube, de la carte, des abonnés ... dans une fonction et non des fonctions d'appel, dans ce cas, "ceci" est en train de changer, donc la portée de la variable est la solution
Skander Jenhani
@SkanderJenhani ce commentaire a 8 ans. Ma réponse serait très différente aujourd'hui
michael le
18

Ouais, cela semble être une norme courante. Certains codeurs utilisent eux-mêmes, d'autres m'utilisent. Il est utilisé comme référence à l'objet «réel» par opposition à l'événement.

C'est quelque chose qui m'a pris un peu de temps pour vraiment comprendre, cela semble étrange au début.

Je fais généralement cela juste en haut de mon objet (excusez mon code de démonstration - c'est plus conceptuel qu'autre chose et n'est pas une leçon sur une excellente technique de codage):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}
BenAlabaster
la source
12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

edit: malgré, les fonctions imbriquées dans un objet prennent l'objet de fenêtre globale plutôt que l'objet environnant.

Serkan
la source
Vous semblez décrire ce qui var self = thisfait, mais ce n'est pas ce que la question pose (la question est de savoir si c'est la meilleure solution au problème).
Quentin
3
"Le problème central est de savoir comment" canaliser "une variable dans les fonctions intégrées." Je suis d'accord avec cette affirmation dans la réponse acceptée. En plus de cela, il n'y a rien de mal à «cette» pratique. C'est assez courant. Donc, en réponse à la partie "le code me semble étrange", j'ai choisi d'expliquer comment cela fonctionne. Je crois que la raison pour laquelle "le code me semble étrange" vient de la confusion quant à son fonctionnement.
serkan
12

Si vous faites ES2015 ou tapez script et ES5, vous pouvez utiliser les fonctions fléchées dans votre code et vous ne faites pas face à cette erreur et cela fait référence à la portée souhaitée dans votre instance.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});
Aran Dekar
la source
5
En guise d'explication: dans ES2015, les fonctions fléchées capturent à thispartir de leur champ d'application. Les définitions de fonction normales ne font pas cela.
defnull le
@defnull Je ne pense pas que ce soit quelque chose de spécial. Les fonctions créent une étendue de bloc. donc utiliser la notation fléchée au lieu d'une fonction vous empêche de créer une portée, donc, cela fera toujours référence à la variable externe de cette variable.
Nimeshka Srimal le
4

Une solution à cela est de lier tous vos rappels à votre objet avec la bindméthode javascript .

Vous pouvez le faire avec une méthode nommée,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Ou avec un rappel anonyme

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Faire cela au lieu de recourir à var self = thismontre que vous comprenez comment la liaison de thisse comporte en javascript et ne repose pas sur une référence de fermeture.

En outre, l'opérateur de grosse flèche dans ES6 est fondamentalement le même qu'un appel .bind(this)à une fonction anonyme:

doCallback( () => {
  // You can reference "this" here now
});
Bart Dorsey
la source
Personnellement, je pense que .bind (this) est juste plus typé, changer la référence this à moi (ou autre) et l'utiliser simplement à la place pour un code plus propre. La grosse flèche est le futur tho.
davidbuttar
3

Je n'ai pas utilisé jQuery, mais dans une bibliothèque comme Prototype, vous pouvez lier des fonctions à une portée spécifique. Donc, avec cela à l'esprit, votre code ressemblerait à ceci:

 $('#foobar').ready('click', this.doSomething.bind(this));

La méthode bind renvoie une nouvelle fonction qui appelle la méthode d'origine avec la portée que vous avez spécifiée.

néonski
la source
Analogue à: $('#foobar').ready('click', this.doSomething.call(this));non?
Muers
La bindméthode ne fonctionne pas dans les navigateurs plus anciens, utilisez donc $.proxyplutôt la méthode.
Guffa
2

En ajoutant simplement à cela, dans ES6 en raison des fonctions de flèche, vous ne devriez pas avoir besoin de le faire car elles capturent la thisvaleur.

août
la source
1

Je pense que cela dépend en fait de ce que vous allez faire dans votre doSomethingfonction. Si vous allez accéder aux MyObjectpropriétés à l'aide de ce mot-clé, vous devez l'utiliser. Mais je pense que le fragment de code suivant fonctionnera également si vous ne faites rien de spécial en utilisant les object(MyObject)propriétés.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Scott Harrison
la source