CoffeeScript, Quand utiliser la flèche grasse (=>) sur la flèche (->) et vice versa

Lors de la construction d’une classe dans CoffeeScript, toute la méthode d’instance doit-elle être définie en utilisant l’opérateur => (“fat arrow”) et toutes les méthodes statiques étant définies en utilisant l’opérateur -> ?

Non, ce n’est pas la règle que j’utiliserais.

Le principal cas d’utilisation que j’ai trouvé pour la grosse flèche dans la définition des méthodes est lorsque vous souhaitez utiliser une méthode en tant que rappel et que cette méthode référence les champs d’instance:

 class A constructor: (@msg) -> thin: -> alert @msg fat: => alert @msg x = new A("yo") x.thin() #alerts "yo" x.fat() #alerts "yo" fn = (callback) -> callback() fn(x.thin) #alerts "undefined" fn(x.fat) #alerts "yo" fn(-> x.thin()) #alerts "yo" 

Comme vous le voyez, vous pouvez rencontrer des problèmes en transmettant une référence à la méthode d’une instance en tant que rappel si vous n’utilisez pas la flèche en gras. En effet, la grosse flèche lie l’instance de l’object à this alors que la flèche mince ne le fait pas. Les méthodes de flèche fine appelées callbacks ci-dessus ne peuvent donc pas accéder aux champs tels que @msg ou appeler d’autres méthodes d’instance. La dernière ligne contient une solution de contournement pour les cas où la flèche fine a été utilisée.

Un point qui n’est pas mentionné dans les autres réponses et qui est important à noter est que les fonctions de liaison avec une flèche grasse lorsque cela n’est pas nécessaire peuvent conduire à des résultats inattendus comme dans cet exemple avec une classe que nous appellerons DummyClass.

 class DummyClass constructor : () -> some_function : () -> return "some_function" other_function : () => return "other_function" dummy = new DummyClass() dummy.some_function() == "some_function" # true dummy.other_function() == "other_function" # true 

Dans ce cas, les fonctions font exactement ce à quoi on peut s’attendre et il ne semble pas y avoir de perte à la flèche grasse, mais que se passe-t-il quand on modifie le prototype DummyClass après en avoir modifié :

 DummyClass::some_function = -> return "some_new_function" DummyClass::other_function = -> return "other_new_function" dummy.some_function() == "some_new_function" # true dummy.other_function() == "other_new_function" # false dummy.other_function() == "other_function" # true 

Comme nous pouvons le voir, la fonction précédemment définie du prototype provoque le remplacement correct de some_function, mais d’autres_fonctions restnt les mêmes sur les instances car la grosse flèche a lié l’autre classe à toutes les instances, donc les instances ne renvoient pas à leur classe. trouver une fonction

 DummyClass::other_function = => return "new_other_new_function" dummy.other_function() == "new_other_new_function" # false second_dummy = new DummyClass() second_dummy.other_function() == "new_other_new_function" # true 

Même les grosses flèches ne fonctionneront pas car les grosses flèches ne font que lier la fonction à de nouvelles instances (qui acquièrent les nouvelles fonctions comme on peut s’y attendre).

Cependant, cela entraîne certains problèmes, et si nous avons besoin d’une fonction (par exemple, dans le cas du basculement d’une fonction de journalisation vers une zone de sortie ou quelque chose) qui fonctionne sur toutes les instances existantes (y compris les gestionnaires d’événements). les grosses flèches dans la définition d’origine] mais nous avons toujours besoin d’accéder aux atsortingbuts internes dans un gestionnaire d’événements [la raison exacte pour laquelle nous avons utilisé des flèches grasses, pas des flèches fines].

Eh bien, la manière la plus simple d’y parvenir est d’inclure simplement deux fonctions dans la définition de classe d’origine, l’une définie par une flèche fine qui effectue les opérations que vous souhaitez exécuter et l’autre définie par une flèche grasse qui n’appelle que la première fonction. par exemple:

 class SomeClass constructor : () -> @data = 0 _do_something : () -> return @data do_something : () => @_do_something() something = new SomeClass() something.do_something() == 0 # true event_handler = something.do_something event_handler() == 0 # true SomeClass::_do_something = -> return @data + 1 something.do_something() == 1 # true event_handler() == 1 # true 

Donc, quand utiliser des flèches minces / grasses peut être résumé assez facilement de quatre manières:

  1. Les seules fonctions de flèche mince doivent être utilisées lorsque les deux conditions sont remplies:

    • La méthode ne sera jamais transmise par référence, y compris event_handlers, par exemple vous n’avez jamais un cas tel que: some_reference = some_instance.some_method; some_reference ()
    • ET la méthode devrait être universelle sur toutes les instances, donc si la fonction prototype change, la méthode l’est aussi pour toutes les instances
  2. Les seules fonctions de la flèche grasse doivent être utilisées lorsque la condition suivante est remplie:

    • La méthode doit être liée précisément à l’instance lors de la création de l’instance et restr liée en permanence même si la définition de la fonction change pour le prototype, cela inclut tous les cas où la fonction doit être un gestionnaire d’événement et le comportement du gestionnaire d’événement
  3. La fonction flèche volumineuse qui appelle directement une fonction flèche fine doit être utilisée lorsque les conditions suivantes sont remplies:

    • La méthode doit être appelée par référence, par exemple un gestionnaire d’événement
    • ET la fonctionnalité peut changer à l’avenir en affectant les instances existantes en remplaçant la fonction de flèche mince
  4. La fonction flèche mince qui appelle directement une fonction de flèche grasse (non démontrée) doit être utilisée lorsque les conditions suivantes sont remplies:

    • La fonction de flèche grasse doit toujours être attachée à l’instance
    • MAIS la fonction de flèche mince peut changer (même pour une nouvelle fonction qui n’utilise pas la fonction de flèche en gras d’origine)
    • ET la fonction de flèche mince n’a jamais besoin d’être transmise par référence

Dans toutes les approches, il faut tenir compte du fait que les fonctions prototypes peuvent être modifiées, que le comportement d’instances spécifiques se comporte ou non correctement, par exemple, si une fonction est définie avec une grosse flèche. une méthode qui est modifiée dans le prototype

Habituellement, -> c’est bien.

 class Foo @static: -> this instance: -> this alert Foo.static() == Foo # true obj = new Foo() alert obj.instance() == obj # true 

Notez que la méthode statique renvoie l’object de classe pour this et que l’instance retourne l’object d’instance correspondant.

Ce qui se passe, c’est que la syntaxe d’invocation en fournit la valeur. Dans ce code:

 foo.bar() 

foo sera le contexte de la fonction bar() par défaut. Donc ça marche comme tu veux. Vous n’avez besoin que de la grosse flèche lorsque vous appelez ces fonctions d’une manière qui n’utilise pas la syntaxe à points pour l’invocation.

 # Pass in a function reference to be called later # Then later, its called without the dot syntax, causing `this` to be lost setTimeout foo.bar, 1000 # Breaking off a function reference will lose it's `this` too. fn = foo.bar fn() 

Dans les deux cas, utiliser une grosse flèche pour déclarer cette fonction permettrait à ceux-ci de fonctionner. Mais à moins que vous ne fassiez quelque chose d’étrange, vous n’avez généralement pas besoin de le faire.

Donc, utilisez -> jusqu’à ce que vous ayez vraiment besoin de => et n’utilisez jamais => par défaut.

juste un exemple pour ne pas respecter la flèche de graisse

ne fonctionne pas: (@canvas undefined)

 class Test constructor: -> @canvas = document.createElement 'canvas' window.addEventListener 'resize', -> @canvas.width = window.innerWidth @canvas.height = window.innerHeight 

fonctionne: (@canvas défini)

 class Test constructor: -> @canvas = document.createElement 'canvas' window.addEventListener 'resize', => @canvas.width = window.innerWidth @canvas.height = window.innerHeight