Ответ 1
Да, есть причины не использовать жирные стрелки всегда. На самом деле я бы утвердил, что никогда не использовал методы с жирными стрелками:)
Методы тонкой стрелки и жирной стрелки концептуально разные. Первые компилируются в ожидаемый код JS на основе прототипов; методы принадлежат прототипу класса. С другой стороны, методы со стрелками со стрелками связаны с каждым экземпляром кода конструктора.
Наиболее очевидным недостатком всегда использования методов с жирными стрелками является то, что он заставляет каждый экземпляр класса занимать больше памяти (потому что он имеет больше собственных свойств), а его инициализация медленнее (потому что он должен создавать эти связанные функции и устанавливать их каждый время создается экземпляр).
Другим недостатком использования методов толстой стрелки является то, что он нарушает обычное ожидание того, что такое метод: метод больше не является функцией, разделяемой между экземплярами класса, но теперь это отдельная функция для каждого экземпляра. Это может вызвать проблемы, если, например, вы захотите изменить метод после того, как он был определен в классе:
class Foo
# Using fat-arrow method
bar: (x) => alert x
# I have some Foos
foos = (new Foo for i in [1..3])
# And i want to path the bar method to add some logging.
# This might be in another module or file entirely.
oldbar = Foo::bar
Foo::bar = (args...) ->
console.log "Foo::bar called with", args
oldbar.apply @, args
# The console.log will never be called here because the bar method
# has already been bound to each instance and was not modified by
# the above patch.
foo.bar(i) for foo, i in foos
Но самый важный недостаток, на мой взгляд, более субъективен: введение методов толстой стрелки делает код (и язык) излишне непоследовательным и трудным для понимания.
Код становится более непоследовательным, потому что перед тем, как вводить методы толстой стрелки в любое время, когда мы видим <someProp>: <someVal>
в определении класса, мы знаем, что это означает "объявить свойство с именем <someProp>
со значением <someVal>
в прототипе класса" (если <someProp> == 'constructor'
, что является особым случаем), не имеет значения, является ли <someVal>
числом или функцией, это будет просто свойство в прототипе. С внедрением методов с жирными стрелками у нас теперь есть еще один ненужный частный случай: если <someVal>
- функция с жирными стрелками, это будет совершенно иначе, чем с любым другим значением.
И там другая несогласованность: жирные стрелки по-разному связывают this
, когда они используются в определении метода, чем при использовании где-либо еще. Вместо сохранения внешнего this
(который внутри a class
, this
привязан к конструктору класса) this
внутри метода с жирными стрелками является объектом, который не существует, когда метод определен ( т.е. экземпляр класса).
Если вы смешиваете методы со стрелками со стрелками и жирами, код также становится более сложным, потому что теперь каждый раз, когда разработчик видит метод с жирными стрелками, они спрашивают себя, зачем ему нужно, чтобы этот метод был экземпляром, связаны. Там нет непосредственной корреляции между декларацией метода и тем, где она используется, где возникает потребность в методе толстой стрелки.
При всем этом я бы рекомендовал никогда не использовать методы толстой стрелки. Предпочитаете привязать метод к экземпляру, где он будет использоваться вместо того, где объявлен метод. Например:
# Be explicit about 'onClick' being called on 'someObject':
$someJQueryElement.on 'click', (e) -> someObject.onClick e
# Instead of:
$someJQueryElement.on 'click', someObject.onClick
Или, если вы действительно хотите привязать метод к каждому экземпляру во время построения, будьте откровенны в этом:
# Instead of fat-arrow methods:
class A
constructor: ->
@bar = 42
foo: =>
console.log @bar
# Assing the method in the constructor, just like you would
# do with any other own property
class A
constructor: ->
@bar = 42
@foo = =>
console.log @bar
Я думаю, что во втором определении class A
это гораздо более явное, что происходит с методом foo
, чем в первом определении.
Наконец, обратите внимание, что я не спорю против использования толстой стрелки вообще. Это очень полезная конструкция, и я все время использую ее для обычных функций; я просто предпочитаю избегать использования его внутри определения метода class
:)
Изменить. Еще один случай против использования методов с жирными стрелками: функции декоратора:
# A decorator function to profile another function.
profiled = (fn) ->
(args...) ->
console.profile()
fn.apply @, args
console.profileEnd()
class A
bar: 10
# This works as expected
foo: profiled (baz) ->
console.log "@bar + baz:", @bar + baz
# This doesn't
fatArrowedFoo: profiled (baz) =>
console.log "@bar + baz:", @bar + baz
(new A).foo 5 # -> @bar + baz: 15
(new A).fatArrowedFoo 5 # -> @bar + baz: NaN