convertir - CoffeeScript, cuándo usar la flecha de grasa(=>) sobre la flecha(->) y viceversa
js to coff (4)
No, esa no es la regla que usaría.
El principal caso de uso que he encontrado para la flecha adiposa en la definición de métodos es cuando desea usar un método como devolución de llamada y ese método hace referencia a los campos de instancia:
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"
Como puede ver, puede tener problemas al pasar una referencia al método de una instancia como una devolución de llamada si no usa la flecha adiposa. Esto se debe a que la flecha adherente vincula la instancia del objeto con this
mientras que la flecha delgada no, por lo que los métodos de flecha delgada llamados devoluciones de llamada como los anteriores no pueden acceder a los campos de la instancia como @msg
o llamar a otros métodos de instancia. La última línea es una solución alternativa para los casos en los que se ha utilizado la flecha delgada.
Al construir una clase en CoffeeScript, ¿debería definirse todo el método de la instancia utilizando el operador =>
("flecha adiposa") y todos los métodos estáticos que se definen con el operador ->
?
Por lo general, ->
está bien.
class Foo
@static: -> this
instance: -> this
alert Foo.static() == Foo # true
obj = new Foo()
alert obj.instance() == obj # true
Observe cómo el método estático devuelve el objeto de clase para this
y la instancia devuelve el objeto de instancia para this
.
Lo que sucede es que la sintaxis de invocación proporciona el valor de this
. En este código:
foo.bar()
foo
será el contexto de la función bar()
por defecto. Por lo tanto, simplemente funciona como quieras. Solo necesita la flecha adiposa cuando llama a estas funciones de alguna otra manera que no usa la sintaxis de puntos para la invocación.
# 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()
En ambos casos, usar una flecha adiposa para declarar esa función permitiría que esas personas trabajen. Pero a menos que esté haciendo algo extraño, generalmente no es necesario.
Entonces usa ->
hasta que realmente necesites =>
y nunca uses =>
por defecto.
Un punto no mencionado en otras respuestas que es importante tener en cuenta es que las funciones de enlace con la flecha adiposa cuando no es necesaria pueden dar lugar a resultados no deseados, como en este ejemplo con una clase a la que simplemente llamaremos 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
En este caso, las funciones hacen exactamente lo que cabría esperar y parece que no hay pérdidas en el uso de la flecha adiposa, pero ¿qué sucede cuando modificamos el prototipo DummyClass una vez definido? (Por ejemplo, cambiando alguna alerta o cambiando el resultado de un registro) :
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
Como podemos ver anulando nuestra función del prototipo previamente definida, se sobrescribe correctamente una función, pero la función other_function permanece igual en casos en que la flecha fat ha causado que other_function de la clase se vincule a todas las instancias, por lo que las instancias no harán referencia a su clase para encontrar una función
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
Incluso la flecha adiposa no funcionará ya que la flecha adiposa solo hace que la función se vincule a las nuevas instancias (que sí obtienen las nuevas funciones como cabría esperar).
Sin embargo, esto lleva a algunos problemas, ¿qué pasa si necesitamos una función (por ejemplo, en el caso de cambiar una función de registro a un cuadro de salida o algo) que funcione en todas las instancias existentes (incluidos los controladores de eventos) [como tales no podemos usar flechas gordas en la definición original], pero aún necesitamos acceso a atributos internos en un controlador de eventos [la razón exacta por la que usamos flechas gordas, no flechas delgadas].
Bueno, la forma más simple de lograr esto es simplemente incluir dos funciones en la definición de clase original, una definida con una flecha delgada que hace las operaciones que desea ejecutar, y otra definida con una flecha adiposa que no hace más que llamar a la primera función por ejemplo:
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
Entonces, cuándo usar flechas finas / gruesas se puede resumir bastante fácil de cuatro maneras:
Deben usarse funciones de flecha delgada solo cuando se cumplen ambas condiciones:
- El método nunca será pasado por referencia, incluyendo event_handlers, por ejemplo, nunca tienes un caso como: some_reference = some_instance.some_method; some_reference ()
- Y el método debe ser universal en todas las instancias, de modo que si la función del prototipo cambia también lo hace el método en todas las instancias
Las funciones solo de la flecha de grasa se deben usar cuando se cumple la siguiente condición:
- El método debe vincularse exactamente a la instancia en la creación de instancia y permanecer permanentemente vinculado incluso si la definición de función cambia para el prototipo, esto incluye todos los casos donde la función debe ser un controlador de eventos y el comportamiento del controlador de eventos debe ser consistente
La función de flecha de grasa que llama directamente a una función de flecha delgada se debe usar cuando se cumplen las siguientes condiciones:
- Se requiere que el método sea llamado por referencia, como un controlador de eventos
- Y la funcionalidad puede cambiar en el futuro afectando las instancias existentes al reemplazar la función de flecha delgada
La función de flecha delgada que llama directamente a la función de flecha adiposa (no demostrada) debe usarse cuando se cumplen las siguientes condiciones:
- La función de flecha de grasa siempre debe estar unida a la instancia
- PERO la función de flecha delgada puede cambiar (incluso a una nueva función que no utiliza la función de flecha original)
- Y la función de flecha delgada nunca se necesita para pasar por referencia
En todos los enfoques, debe considerarse en el caso donde las funciones del prototipo pueden cambiar si el comportamiento de instancias específicas se comportará correctamente, por ejemplo, aunque una función se define con una flecha adherente, su comportamiento puede no ser consistente dentro de una instancia si llama un método que se cambia dentro del prototipo
solo un ejemplo para entender la flecha gordo
no funciona: (@canvas undefined)
class Test
constructor: ->
@canvas = document.createElement ''canvas''
window.addEventListener ''resize'', ->
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight
funciona: (@canvas definido)
class Test
constructor: ->
@canvas = document.createElement ''canvas''
window.addEventListener ''resize'', =>
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight