Pouvez-vous lier «ceci» dans une fonction de flèche?

133

J'expérimente avec ES6 depuis un moment maintenant, et je viens juste d'arriver à un léger problème.

J'aime vraiment utiliser les fonctions fléchées, et chaque fois que je peux, je les utilise.

Cependant, il semblerait que vous ne puissiez pas les lier!

Voici la fonction:

var f = () => console.log(this);

Voici l'objet auquel je souhaite associer la fonction:

var o = {'a': 42};

Et voici comment je me lierais fà o:

var fBound = f.bind(o);

Et puis je peux simplement appeler fBound:

fBound();

Ce qui affichera ceci (l' oobjet):

{'a': 42}

Cool! Charmant! Sauf que ça ne marche pas. Au lieu de sortir l' oobjet, il sort l' windowobjet.

Alors j'aimerais savoir: pouvez-vous lier des fonctions fléchées? (Et si oui, comment?)


J'ai testé le code ci-dessus dans Google Chrome 48 et Firefox 43, et le résultat est le même.

Florrie
la source
29
L'intérêt des fonctions fléchées est qu'elles utilisent le thisde leur étendue parent.
loganfsmyth

Réponses:

158

Vous ne pouvez pas relier this dans une fonction de flèche. Il sera toujours défini comme le contexte dans lequel il a été défini. Si vous avez besoin thisd'être significatif, vous devez utiliser une fonction normale.

D'après les spécifications ECMAScript 2015 :

Toute référence à des arguments, super, this ou new.target dans une ArrowFunction doit être résolue en une liaison dans un environnement englobant lexicalement. Il s'agit généralement de l'environnement de fonction d'une fonction immédiatement englobante.

Nick Tomlin
la source
7
La première phrase de cette réponse est fausse. Probablement parce que la question prête également à confusion this. Les deux sont liés, mais pas les mêmes. thisà l'intérieur d'une fonction de flèche «hérite» toujours thisde la portée englobante. C'est une caractéristique des fonctions fléchées. Mais vous pouvez toujours bindtous les autres paramètres d'une fonction fléchée. Mais non this.
Stijn de Witt
54

Pour être complet, vous pouvez relier les fonctions fléchées, vous ne pouvez tout simplement pas changer la signification de this.

bind a toujours de la valeur pour les arguments de fonction:

((a, b, c) => {
  console.info(a, b, c) // 1, 2, 3
}).bind(undefined, 1, 2, 3)()

Essayez-le ici: http://jsbin.com/motihanopi/edit?js,console

cvazac
la source
1
Existe-t-il un moyen de détecter ou d'utiliser n'importe quel objet thisauquel la fonction (flèche) est liée, sans référence (ce qui est, bien sûr, défini lexicalement)?
Florrie
3
Utilisez un argument pour le contexte: ((context) => {someOtherFunction.apply (context)}). Bind (willBeIgnored, context) ()
cvazac
13

Du MDN :

Une expression de fonction de flèche a une syntaxe plus courte que les expressions de fonction et lie lexicalement la valeur this (ne lie pas son propre this, arguments, super ou new.target). Les fonctions fléchées sont toujours anonymes.

Cela signifie que vous ne pouvez pas lier une valeur à ce thisque vous voulez.

Sterling Archer
la source
6

Pendant des années, les développeurs js ont eu du mal avec la liaison de contexte, ont demandé pourquoi thischangé en javascript, tant de confusion au fil des ans en raison de la liaison de contexte et de la différence entre la signification de thisen javascript et thisdans la plupart des autres langages POO.

Tout cela m'amène à me demander pourquoi, pourquoi! pourquoi ne voudriez-vous pas relier une fonction de flèche! Ceux où créé spécialement pour résoudre tous ces problèmes et confusions et éviter d' avoir à utiliser bindou callou tout autre moyen de préserver la portée de la fonction.

TL; DR

Non, vous ne pouvez pas relier les fonctions fléchées.

taxicala
la source
7
Les fonctions fléchées ont été créées avant tout pour fournir une syntaxe plus propre et plus rapide. Pensez lambda-calcul. Dommage que les zélotes OO qui harcèlent la vie des programmeurs JS fonctionnels ont saisi l'opportunité de leur retirer la liberté de spécifier le contexte d'invocation.
Marco Faustinelli
5
L'utilisation thisn'est pas fonctionnelle. lambdas devrait l'être arguments => output. Si vous avez besoin d'un contexte externe, transmettez-le. L'existence même de thisest ce qui a facilité tout le chaussage des modèles OO dans la langue. Vous n'auriez jamais entendu le terme «classe javascript» sans lui.
erich2k8
3
Vous pouvez relier les fonctions fléchées. Mais non this.
Stijn de Witt
6

Vous ne pouvez pas utiliser bindpour modifier la valeur de l' thisintérieur d'une fonction de flèche. Cependant, vous pouvez créer une nouvelle fonction régulière qui fait la même chose que l'ancienne fonction de flèche, puis utiliser callou bindpour relier thiscomme d'habitude.

Nous utilisons un evalappel ici pour recréer la fonction de flèche que vous passez en tant que fonction normale, puis callpour l'invoquer avec un autre this:

var obj = {value: 10};
function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});

le nom
la source
3
Merci de partager cet exemple d'une manière de lier une fonction de flèche à un contexte différent! Pour rendre la réponse plus facile à comprendre pour les futurs lecteurs, vous pourriez peut-être la modifier pour décrire comment et pourquoi le code fonctionne?
Florrie
4

Les fonctions fléchées ES6 résolvent vraiment «ceci» en JavaScript

Le lien ci-dessus explique que les fonctions fléchées thisne changent pas avec les bind, call, applyfonctions.

C'est expliqué avec un très bel exemple.

exécutez ceci node v4pour voir le comportement "attendu",

this.test = "attached to the module";
var foo = { test: "attached to an object" };
foo.method = function(name, cb){ 
    // bind the value of "this" on the method 
    // to try and force it to be what you want 
    this[name] = cb.bind(this); };
foo.method("bar", () => { console.log(this.test); });
foo.bar(); 
Prithvi Raj Vuppalapati
la source
Vous demander de fournir des informations plus pertinentes dans la réponse elle-même, peut être un exemple de code ou des versions modifiées de questions
Jithin Scaria
// lancez ceci dans le nœud v4 pour voir le comportement "attendu" this.test = "attaché au module"; var foo = {test: "attaché à un objet"}; foo.method = function (name, cb) {// liez la valeur de "this" sur la méthode // pour essayer de la forcer à être ce que vous voulez this [name] = cb.bind (this); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati
Ce qui est intéressant ici, c'est d'essayer ceci et de le réessayer en remplaçant foo.method ('bar', () => {console.log (this.test);}); avec foo.method ('bar', function () {console.log (this.test);}); - la première version des logs "attachés au module" et les seconds logs "attachés à un objet" - Je préfère vraiment le traitement plus stable de "this" en utilisant les fonctions fléchées. Vous pouvez toujours obtenir les autres effets en utilisant différents modèles et les résultats sont plus lisibles et prévisibles IMO.
Méthodicien
3

J'ai posé la même question il y a quelques jours.

Vous ne pouvez pas lier une valeur car le thisest déjà lié.

Liaison différente de cette portée à ES6 => opérateur de fonction

fos.alex
la source
4
"puisque le ceci est déjà lié" - Dans les fonctions régulières thisest également lié. Le fait est que dans les fonctions fléchées, il n'a pas de liaison locale .
un meilleur oliver
2

Bref, vous NE POUVEZ PAS lier les fonctions fléchées, mais lisez la suite:

Imaginez que vous avez cette fonction de flèche ci-dessous qui s'imprime thissur la console:

const myFunc = ()=> console.log(this);

Donc, la solution rapide pour cela serait d'utiliser la fonction normale, alors changez-la simplement en:

function myFunc() {console.log(this)};

Ensuite, vous pouvez le lier à n'importe quel environnement lexical en utilisant bindou callou apply:

const bindedFunc = myFunc.bind(this);

et appelez-le en cas de bind.

bindedFunc();

Il existe également des moyens d'utiliser eval()pour le faire, ce qui est fortement déconseillé .

Alireza
la source
function myFuncest comportementalement différent en plus d'être lié; un match plus proche serait const myFunc = function() {...}. Je suis également curieux de savoir ce que vous entendez en utilisant eval, puisque c'est une approche que je ne pense pas avoir partagée ici auparavant - il serait intéressant de voir comment cela se fait, puis de lire pourquoi c'est si fortement déconseillé.
Florrie le
1

Peut-être que cet exemple vous aidera:

let bob = {
   _name: "Bob",
   _friends: ["stackoverflow"],
   printFriends:(x)=> {
      x._friends.forEach((f)=> {
         console.log(x._name + " knows " + f);
      });
   }
}

bob.printFriends = (bob.printFriends).bind(null,bob);
bob.printFriends();

Ehsan
la source
trucs gøød ... légèrement intérésting
Aleks