Ao criar uma classe no CoffeeScript, todo o método de instância deve ser definido usando o =>
operador ("seta gorda") e todos os métodos estáticos sendo definidos usando o ->
operador?
Ao criar uma classe no CoffeeScript, todo o método de instância deve ser definido usando o =>
operador ("seta gorda") e todos os métodos estáticos sendo definidos usando o ->
operador?
Respostas:
Não, essa não é a regra que eu usaria.
O principal caso de uso que encontrei para a seta gorda na definição de métodos é quando você deseja usar um método como retorno de chamada e esse método faz referência a campos de instância:
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 você vê, você pode ter problemas ao passar uma referência ao método de uma instância como um retorno de chamada se você não usar a seta de gordura. Isso ocorre porque a seta gorda vincula a instância do objeto, ao this
passo que a seta fina não, então os métodos de seta fina chamados como retornos de chamada como acima não podem acessar os campos da instância @msg
ou chamar outros métodos de instância. A última linha existe uma solução alternativa para os casos em que a seta fina foi usada.
this
que seria chamado a partir da seta fina, mas também as variáveis de instância que você obteria com a seta gorda?
this
definido como uma variável que eu quero usar. No entanto, também quero fazer referência a um método de classe, então também quero this
me referir à classe. Só posso escolher entre uma tarefa para this
; então, qual é a melhor maneira de poder usar as duas variáveis?
Um ponto não mencionado em outras respostas que é importante observar é que as funções de ligação com a seta gorda quando não são necessárias podem levar a resultados indesejados, como neste exemplo com uma classe que chamaremos de 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
Nesse caso, as funções fazem exatamente o que se pode esperar e parece não haver perda no uso da seta gorda, mas o que acontece quando modificamos o protótipo DummyClass depois que ele já foi definido (por exemplo, alterar algum alerta ou alterar a saída de um log) :
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, substituir nossa função definida anteriormente do protótipo faz com que alguma função seja substituída corretamente, mas outra função permaneça a mesma nas instâncias, pois a seta gorda fez com que outra função da classe seja vinculada a todas as instâncias, para que as instâncias não se refiram à sua classe encontrar uma função
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
Mesmo a seta gorda não funcionará, uma vez que a seta gorda apenas faz com que a função seja vinculada a novas instâncias (que ganham as novas funções como seria de esperar).
No entanto, isso leva a alguns problemas, e se precisarmos de uma função (por exemplo, no caso de alternar uma função de log para uma caixa de saída ou algo assim) que funcione em todas as instâncias existentes (incluindo manipuladores de eventos) [como tal, não podemos usar setas gordas na definição original], mas ainda precisamos acessar atributos internos em um manipulador de eventos [o motivo exato pelo qual usamos setas gordas e não finas].
Bem, a maneira mais simples de conseguir isso é incluir apenas duas funções na definição de classe original, uma definida com uma seta fina que executa as operações que você deseja executar e outra definida com uma seta gorda que não faz nada além de chamar a primeira função por exemplo:
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
Portanto, quando usar flechas finas / gordas pode ser resumido facilmente de quatro maneiras:
As funções de seta fina por si só devem ser usadas quando as duas condições forem atendidas:
As funções de seta gorda sozinhas devem ser usadas quando a seguinte condição for atendida:
A função de seta gorda que chama diretamente uma função de seta fina deve ser usada quando as seguintes condições forem atendidas:
A função de seta fina que chama diretamente uma função de seta gorda (não demonstrada) deve ser usada quando as seguintes condições forem atendidas:
Em todas as abordagens, deve ser considerado no caso em que as funções do protótipo possam ser alteradas, se o comportamento de instâncias específicas se comportará ou não corretamente, por exemplo, embora uma função seja definida com uma seta gorda, seu comportamento pode não ser consistente em uma instância, se chamar um método que é alterado dentro do protótipo
Geralmente, ->
está bem.
class Foo
@static: -> this
instance: -> this
alert Foo.static() == Foo # true
obj = new Foo()
alert obj.instance() == obj # true
Observe como o método estático retorna o objeto de classe this
e a instância retorna o objeto de instância this
.
O que está acontecendo é que a sintaxe de chamada está fornecendo o valor de this
. Neste código:
foo.bar()
foo
será o contexto da bar()
função por padrão. Então isso meio que funciona como você quer. Você só precisa da seta gorda quando chama essas funções de alguma outra maneira que não usa a sintaxe de pontos para invocação.
# 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()
Nos dois casos, usar uma seta gorda para declarar essa função permitiria que eles funcionassem. Mas, a menos que você esteja fazendo algo estranho, geralmente não precisa.
Portanto, use ->
até que você realmente precise =>
e nunca use =>
por padrão.
x = obj.instance; alert x() == obj # false!
=>
seria necessário um método estático / instância de uma classe.
// is not a CoffeeScript comment
considerando # is a CoffeeScript comment
.
setTimeout foo.bar, 1000
"fazendo errado"? Usar uma flecha gorda é muito melhor do que usar setTimeout (-> foo.bar()), 1000
IMHO.
setTimeout
, é claro. Mas seu primeiro comentário é um tanto artificial e não revela um caso de uso legítimo, mas simplesmente revela como ele pode se romper. Estou simplesmente dizendo que você não deve usar um a =>
menos que precise dele por um bom motivo, especialmente em métodos de instância de classe em que ele tem um custo de desempenho para criar uma nova função que precisa estar vinculada à instanciação.
apenas um exemplo para destacar a seta gorda
não funciona: (@canvas undefined)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', ->
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight
works: (@canvas definido)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', =>
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight