Qu'est-ce qui sous-tend cet idiome JavaScript: var self = this?

357

J'ai vu ce qui suit dans la source de WebKit HTML 5 SQL Storage Notes Demo :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

L'auteur utilise self à certains endroits (le corps de la fonction) et cela à d'autres endroits (les corps des fonctions définies dans la liste des méthodes des arguments). Que se passe-t-il? Maintenant que je l'ai remarqué une fois, vais-je commencer à le voir partout?

Thomas L Holaday
la source
4
Il s'agit d'une fonctionnalité du langage JS appelée «fermeture lexicale».
subZero
2
Copie possible de: var self = this? .
DavidRR
le concept de CECI est expliqué explicitement ici scotch.io/@alZami/understanding-this-in-javascript
AL-zami
Exemple pertinent dans cette réponse stackoverflow.com/a/20279485/5610569 (à la question "Comment accéder au bon thisdans un rappel?")
FluxLemur

Réponses:

431

Voir cet article sur alistapart.com . (Ed: L'article a été mis à jour depuis le lien d'origine)

selfest utilisé pour conserver une référence à l'original thismême lorsque le contexte change. C'est une technique souvent utilisée dans les gestionnaires d'événements (en particulier dans les fermetures).

Edit: Notez que l'utilisation selfest maintenant déconseillée comme elle window.selfexiste et peut entraîner des erreurs si vous ne faites pas attention.

Ce que vous appelez la variable n'a pas d'importance particulière. var that = this;c'est bien, mais le nom n'a rien de magique.

Les fonctions déclarées dans un contexte (par exemple les rappels, les fermetures) auront accès aux variables / fonctions déclarées dans la même portée ou au-dessus.

Par exemple, un simple rappel d'événement:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});

Jonathan Fingland
la source
Apparaît que l'article s'est transformé en utilisationvar that = this;
Bob Stein
@BobStein Merci. Je mettrai à jour la réponse en conséquence.
Jonathan Fingland
96

Je pense que le nom de variable «self» ne devrait plus être utilisé de cette façon, car les navigateurs modernes fournissent une variable globaleself pointant vers l'objet global d'une fenêtre normale ou d'un WebWorker.

Pour éviter la confusion et les conflits potentiels, vous pouvez écrire var thiz = thisou à la var that = thisplace.

Duan Yao
la source
43
J'utilise habituellement_this
djheru
6
@djheru +1. tellement mieux que " that" (auquel mon cerveau ne s'habituera jamais).
o_o_o--
9
J'ai commencé à utiliser "moi" :)
Mopparthy Ravindranath
3
Jusqu'à ce que les navigateurs modernes commencent à fournir une variable globale _ceci, ça ou moi.
Beejor
10
Il n'y a absolument aucun problème à utiliser le nom selftant que vous le déclarez comme un variable, il masquera le global. Bien sûr, si vous avez oublié le, varcela ne fonctionnerait pas non plus avec un autre nom.
Bergi
34

Oui, vous le verrez partout. C'est souvent that = this;.

Voir comment selfest utilisé à l'intérieur des fonctions appelées par les événements? Celles-ci auraient leur propre contexte, elles sont donc selfutilisées pour contenir celles thisqui sont entrées Note().

La raison selfpour laquelle les fonctions sont toujours disponibles, même si elles ne peuvent s'exécuter qu'une fois l'exécution de la Note()fonction terminée, est que les fonctions internes obtiennent le contexte de la fonction externe en raison de la fermeture .

Nosredna
la source
12
Pour moi, le point majeur est que cela selfn'a pas de signification particulière. Personnellement, je préfère utiliser un var nommé autre chose que selfcar cela me confond souvent, car je m'attends à ce que «moi» soit un mot réservé. Donc j'aime votre réponse. Et dans l'exemple de l'OP, je préfère var thisNote = thisou similaire.
steve
@steve a accepté, bien que j'essaie d'éviter d'utiliser ces références / auto en général car elles sont très fragiles en termes de maintenabilité.
mattLummus
28

Il convient également de noter qu'il existe un autre modèle de proxy pour conserver une référence à l'original thisdans un rappel si vous n'aimez pas l' var self = thisidiome.

Comme une fonction peut être appelée avec un contexte donné en utilisant function.applyou function.call, vous pouvez écrire un wrapper qui renvoie une fonction qui appelle votre fonction avec applyou en callutilisant le contexte donné. Voir la proxyfonction de jQuery pour une implémentation de ce modèle. Voici un exemple d'utilisation:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFuncpeut alors être appelé et aura votre version de thiscomme contexte.

Max
la source
10

Comme d'autres l'ont expliqué, var self = this;permet au code dans une fermeture de se référer à la portée parent.

Cependant, nous sommes maintenant en 2018 et ES6 est largement pris en charge par tous les principaux navigateurs Web. L' var self = this;idiome n'est pas aussi essentiel qu'il l'était autrefois.

Il est maintenant possible d'éviter var self = this;grâce à l'utilisation des fonctions fléchées .

Dans les cas où nous aurions utilisé var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Nous pouvons maintenant utiliser une fonction flèche sans var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

Les fonctions fléchées n'ont pas les leurs thiset assument simplement la portée englobante.

Elliot B.
la source
Ou - choc, horreur! - pourquoi ne pas transmettre l'argument pertinent à votre fonction (clôture)? Pourquoi diable faites-vous référence à un état hors champ, pourquoi diable est-ce que quelqu'un programme comme ça? Il n'y a jamais vraiment de raison de le faire. Au lieu de cela, .addEventListender("click", (x) => { console.log(x); });vous avez expliqué très clairement comment et pourquoi, et je suis d'accord que l'utilisation des fonctions fléchées est plus logique, mais quand même ... c'est juste une programmation terrible, paresseuse, désordonnée.
Benjamin R
2
En JavaScript, revenir à l'étendue parent est extrêmement courant et nécessaire. C'est une partie fondamentale de la langue. Votre suggestion de passer dans la portée parent comme argument sur le gestionnaire d'événements n'est pas réellement possible. De plus, dans ES6, les fonctions fléchées utilisent la portée lexicale - «ceci» fait référence à sa portée actuelle et pas plus - ce n'est pas «faire référence à un état hors de portée» ou quelque chose du genre.
Elliot B.
9

La variable est capturée par les fonctions en ligne définies dans la méthode. thisdans la fonction fera référence à un autre objet. De cette façon, vous pouvez faire en sorte que la fonction contienne une référence à thisdans la portée externe.

Mehrdad Afshari
la source
9

C'est une bizarrerie JavaScript. Lorsqu'une fonction est une propriété d'un objet, mieux appelée méthode, cela fait référence à l'objet. Dans l'exemple d'un gestionnaire d'événements, l'objet conteneur est l'élément qui a déclenché l'événement. Lorsqu'une fonction standard est invoquée, cela fait référence à l'objet global. Lorsque vous avez des fonctions imbriquées comme dans votre exemple, cela n'a aucun rapport avec le contexte de la fonction externe. Les fonctions internes partagent la portée avec la fonction conteneur , donc les développeurs utiliseront des variations de var that = thisafin de préserver ce dont ils ont besoin dans la fonction interne.

kombat
la source
5

En fait, self est une référence à window ( window.self) donc quand vous dites que var self = 'something'vous remplacez une référence de fenêtre à lui-même - parce que self existe dans l'objet window.

Voilà pourquoi la plupart des développeurs préfèrent var that = thisplusvar self = this;

En tous cas; var that = this;n'est pas conforme à la bonne pratique ... en supposant que votre code sera révisé / modifié ultérieurement par d'autres développeurs, vous devez utiliser les normes de programmation les plus courantes en ce qui concerne la communauté des développeurs

Par conséquent, vous devriez utiliser quelque chose comme var oldThis/ var oThis/ etc - pour être clair dans votre portée // .. ce n'est pas tant que ça, mais vous économiserez quelques secondes et quelques cycles cérébraux

SorinN
la source
2
@prior Je pense que cela a du sens jusqu'au dernier paragraphe.
miphe
0

Comme mentionné plusieurs fois ci-dessus, «soi» est simplement utilisé pour garder une référence à «ceci» avant d'entrer dans la fonction. Une fois dans la fonction «ceci» se réfère à autre chose.

Cyprien
la source
@JohnPaul ... ce ne fournir une réponse. Ce n'est peut-être pas la bonne réponse, mais comment pouvez-vous dire que "il est utilisé pour ..." n'est pas une réponse à "pourquoi a-t-il fait cela"?
GreenAsJade
-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);

ht zhao
la source
1
Vous devez ajouter une explication avec du code indiquant ce que vous avez fait de spécial.
Farhana