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?
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.
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:
Les fonctions de flèche mince seule doivent être utilisées lorsque les deux conditions sont remplies:
Les fonctions de flèche de graisse seule doivent être utilisées lorsque la condition suivante est remplie:
La fonction Flèche grasse qui appelle directement une fonction Flèche fine doit être utilisée lorsque les conditions suivantes sont remplies:
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:
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
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.
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