web-dev-qa-db-fra.com

CoffeeScript, Quand utiliser la grosse flèche (=>) 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 à l'aide de => ("grosse flèche") et toutes les méthodes statiques définies à l'aide de -> opérateur?

129
Ali Salehi

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

Le cas d'utilisation majeur que j'ai trouvé pour la grosse flèche dans la définition des méthodes est lorsque vous souhaitez utiliser une méthode comme 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 lors du passage d'une référence à la méthode d'une instance comme rappel si vous n'utilisez pas la grosse flèche. Cela est dû au fait que la grosse flèche lie l'instance de l'objet à this alors que la flèche fine ne le fait pas, donc les méthodes de flèche fine appelées comme rappels comme ci-dessus ne peuvent pas accéder aux champs de l'instance comme @msg ou appelez 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.

152
nicolaskruchten

Un point non mentionné dans d'autres réponses qu'il est important de noter est que les fonctions de liaison avec une flèche grasse lorsqu'elle n'est pas nécessaire peuvent conduire à des résultats inattendus, comme dans cet exemple avec une classe que nous appellerons simplement 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 pourrait s'attendre et il ne semble pas y avoir de perte à l'utilisation de la grosse flèche, mais ce qui se passe lorsque nous modifions le prototype DummyClass après qu'il a déjà été défini (par exemple, changer une alerte ou changer la sortie d'un journal) :

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, le fait de remplacer notre fonction précédemment définie du prototype entraîne l'écrasement correct de some_function mais other_function reste le même sur les instances car la grosse flèche a contraint other_function de la classe à être lié à toutes les instances afin que les instances ne se réfèrent 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 la grosse flèche ne fonctionnera pas car la grosse flèche ne fait que lier la fonction à de nouvelles instances (qui gagnent les nouvelles fonctions comme on pourrait s'y attendre).

Cependant, cela entraîne certains problèmes, que se passe-t-il si nous avons besoin d'une fonction (par exemple, dans le cas de la commutation d'une fonction de journalisation vers une boîte de sortie ou quelque chose) qui fonctionnera sur toutes les instances existantes (y compris les gestionnaires d'événements) [en tant que tel, nous ne pouvons pas utiliser flèches grasses dans la définition d'origine] mais nous avons toujours besoin d'accéder aux attributs internes dans un gestionnaire d'événements [la raison exacte pour laquelle nous avons utilisé des flèches grasses et non des flèches fines].

Eh bien, le moyen le plus simple d'accomplir cela est d'inclure simplement deux fonctions dans la définition de classe d'origine, l'une définie avec une flèche fine qui effectue les opérations que vous souhaitez exécuter, et l'autre définie avec une flèche grasse qui ne fait qu'appeler 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

Ainsi, quand utiliser des flèches fines/grosses peut être résumé assez facilement de quatre manières:

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

    • La méthode ne sera jamais transmise par référence, y compris les gestionnaires d'événements, par exemple vous n'avez jamais de cas comme: some_reference = some_instance.some_method; some_reference ()
    • ET la méthode doit être universelle sur toutes les instances, donc si la fonction prototype change, la méthode sur toutes les instances aussi
  2. Les fonctions de flèche de graisse seule 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 rester liée en permanence même si la définition de fonction change pour le prototype, cela inclut tous les cas où la fonction doit être un gestionnaire d'événements et le comportement du gestionnaire d'événements doit être cohérent.
  3. La fonction Flèche grasse 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, comme un gestionnaire d'événements
    • ET la fonctionnalité peut changer à l'avenir affectant les instances existantes en remplaçant la fonction flèche fine
  4. La fonction de flèche mince qui appelle directement une fonction de flèche de graisse (non démontrée) doit être utilisée lorsque les conditions suivantes sont remplies:

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

Dans toutes les approches, il doit être pris en compte dans le cas où les fonctions prototypes pourraient être modifiées, que le comportement pour des instances spécifiques se comporte correctement ou non, par exemple, bien qu'une fonction soit définie avec une grosse flèche, son comportement peut ne pas être cohérent dans une instance si elle appelle une méthode qui est modifiée dans le prototype

13
Jamesernator

Habituellement, -> Est très bien.

class Foo
  @static:  -> this
  instance: -> this

alert Foo.static() == Foo # true

obj = new Foo()
alert obj.instance() == obj # true

Notez comment la méthode statique renvoie l'objet classe pour this et l'instance renvoie l'objet instance pour this.

Ce qui se passe, c'est que la syntaxe d'invocation fournit la valeur de this. Dans ce code:

foo.bar()

foo sera le contexte de la fonction bar() par défaut. Donc ça fonctionne juste comme vous voulez. Vous n'avez besoin de la grosse flèche que lorsque vous appelez ces fonctions d'une autre 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, l'utilisation d'une grosse flèche pour déclarer cette fonction permettrait à ceux-ci de fonctionner. Mais à moins que vous ne fassiez quelque chose de bizarre, vous n'avez généralement pas besoin de le faire.

Utilisez donc -> Jusqu'à ce que vous ayez vraiment besoin de => Et n'utilisez jamais => Par défaut.

9
Alex Wayne

juste un exemple pour la grosse flèche

ne fonctionne pas: (@canvas non défini)

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
5
user3896501