Lors de la construction d'une classe dans CoffeeScript, toute la méthode d'instance doit-elle =>
être définie à l'aide de l' ->
opérateur ("grosse flèche") et toutes les méthodes statiques définies à l'aide de l' opérateur?
Lors de la construction d'une classe dans CoffeeScript, toute la méthode d'instance doit-elle =>
être définie à l'aide de l' ->
opérateur ("grosse flèche") et toutes les méthodes statiques définies à l'aide de l' opérateur?
Réponses:
Non, ce n'est pas la règle que j'utiliserais.
Le cas d'utilisation principal que j'ai trouvé pour la grosse flèche dans la définition de méthodes est lorsque vous souhaitez utiliser une méthode comme rappel et que cette méthode fait référence aux 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 passant une référence à la méthode d'une instance en tant que rappel si vous n'utilisez pas la grosse flèche. Ceci est dû au fait que la grosse flèche lie l'instance de l'objet à, this
contrairement à la flèche fine, 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 appeler d'autres méthodes d'instance. La dernière ligne, il existe une solution de contournement pour les cas où la flèche fine a été utilisée.
this
qui serait appelé à partir de la flèche fine, mais aussi les variables d'instance que vous obtiendriez avec la flèche épaisse?
this
définie sur une variable que je souhaite utiliser. Cependant, je souhaite également faire référence à une méthode de classe, je souhaite donc également this
faire référence à la classe. Je ne peux choisir qu'entre une affectation pour this
, alors quelle est la meilleure façon de pouvoir utiliser les deux variables?
Un point non mentionné dans d'autres réponses et qu'il est important de noter est que la liaison de fonctions avec une grosse flèche lorsque ce n'est pas nécessaire peut 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 à utiliser la grosse flèche, mais que se passe-t-il lorsque nous modifions le prototype DummyClass après qu'il ait 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 remplacement de notre fonction précédemment définie du prototype entraîne le remplacement correct de some_function, mais other_function reste le même sur les instances, car la grosse flèche a entraîné la liaison de other_function de la classe à toutes les instances afin que les instances ne se réfèrent pas à leur classe pour 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 fera que lier la fonction à de nouvelles instances (qui acquièrent les nouvelles fonctions comme on s'y attendrait).
Cependant, cela conduit à des 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 originale] 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, une définie avec une flèche fine qui effectue les opérations que vous souhaitez exécuter, et une autre définie avec une grosse flèche qui ne fait rien d'autre que d'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
Donc, quand utiliser des flèches minces / grasses peut être résumé assez facilement de quatre manières:
Les fonctions de flèche fine seules doivent être utilisées lorsque les deux conditions sont mises:
Les fonctions de la grosse flèche seules doivent être utilisées lorsque la condition suivante est remplie:
La fonction de grosse flèche qui appelle directement une fonction de flèche fine doit être utilisée lorsque les conditions suivantes sont remplies:
La fonction de flèche fine qui appelle directement une fonction de flèche grasse (non démontrée) doit être utilisée lorsque les conditions suivantes sont remplies:
Dans toutes les approches, il doit être considéré dans le cas où les fonctions prototypes pourraient être modifiées si le comportement d'instances spécifiques se comportera 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, ->
c'est 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 de classe pour this
et l'instance renvoie l'objet d'instance pour this
.
Ce qui se passe, c'est que la syntaxe d'appel fournit la valeur de this
. Dans ce code:
foo.bar()
foo
sera le contexte de la bar()
fonction par défaut. Donc, ça marche comme vous le souhaitez. 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 dot 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 ces deux cas, l'utilisation d'une grosse flèche pour déclarer cette fonction leur permettrait de fonctionner. Mais à moins que vous ne fassiez quelque chose d'étrange, vous n'en avez généralement pas besoin.
Utilisez donc ->
jusqu'à ce que vous en ayez vraiment besoin =>
et ne l'utilisez jamais =>
par défaut.
x = obj.instance; alert x() == obj # false!
=>
serait nécessaire sur les méthodes statiques / instance d'une classe.
// is not a CoffeeScript comment
alors que # is a CoffeeScript comment
.
setTimeout foo.bar, 1000
«le faire mal»? Utiliser une grosse flèche est beaucoup plus agréable que d'utiliser setTimeout (-> foo.bar()), 1000
IMHO.
setTimeout
, bien sûr. Mais votre premier commentaire est quelque peu artificiel et ne révèle pas un cas d'utilisation légitime, mais révèle simplement comment il pourrait se casser. Je dis simplement que vous ne devriez pas utiliser a à =>
moins que vous n'en ayez besoin pour une bonne raison, en particulier sur les méthodes d'instance de classe où cela a un coût de performance lié à la création d'une nouvelle fonction qui doit être liée à l'instanciation.
juste un exemple de la grosse flèche
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