Створюючи клас у CoffeeScript, чи слід визначати весь метод примірника за допомогою =>
оператора ("жирова стрілка") та всіх статичних методів, визначених за допомогою ->
оператора?
Створюючи клас у CoffeeScript, чи слід визначати весь метод примірника за допомогою =>
оператора ("жирова стрілка") та всіх статичних методів, визначених за допомогою ->
оператора?
Відповіді:
Ні, це не правило, яким я б користувався.
Основний випадок використання, який я знайшов для жирової стрілки при визначенні методів, - це коли ви хочете використовувати метод як зворотний виклик, і цей метод посилається на екземпляри полів:
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"
Як бачите, у вас можуть виникнути проблеми з передачею посилання на метод екземпляра як зворотний виклик, якщо ви не використовуєте жирову стрілку. Це тому, що жирова стрілка пов'язує екземпляр об'єкта, this
тоді як тонка стрілка не робить, тому методи тонкої стрілки, що називаються зворотними @msg
дзвінками, як вище, не можуть отримати доступ до полів екземпляра, як або викликати інші методи екземпляра. В останньому рядку є вирішення випадків, коли була використана тонка стрілка.
this
що називалося б із тонкої стрілки, а також змінні екземпляри, які ви отримаєте зі стрілкою жиру?
this
встановила змінну, яку я хочу використовувати. Однак я також хочу посилатися на метод класу, тому я хочу this
також посилатися на клас. Я можу вибрати лише одне завдання для this
, тож який найкращий спосіб мати можливість використовувати обидві змінні?
Точка, що не згадується в інших відповідях, що важливо відзначити, - це те, що функції зв’язування зі стрілкою жиру, коли це не потрібно, можуть призвести до ненавмисних результатів, таких як у цьому прикладі з класом, який ми просто назвемо 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
У цьому випадку функції роблять саме те, що можна було очікувати, і, здається, немає втрати при використанні жирової стрілки, але що відбувається, коли ми модифікуємо прототип DummyClass після того, як він уже визначений (наприклад, зміна деякого попередження або зміна виходу журналу) :
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
Як ми бачимо, що переосмислення нашої раніше визначеної функції прототипу призводить до того, що деяка функція буде правильно перезаписана, але функція other_function залишається тією ж, що стосується випадків, оскільки жирова стрілка призвела до того, що other_function з класу буде прив’язана до всіх примірників, тому екземпляри не повертаються до їх класу щоб знайти функцію
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
Навіть жирова стрілка не працюватиме, оскільки жирова стрілка спричиняє прив'язку функції до нових екземплярів (які отримують нові функції, як можна було б очікувати)
Однак це призводить до деяких проблем, що робити, якщо нам потрібна функція (наприклад, у випадку переключення функції реєстрації в поле виводу або щось подібне), яка буде працювати у всіх існуючих екземплярах (включаючи обробники подій) [як такої ми не можемо використовувати жирові стрілки в оригінальному визначенні], але нам все одно потрібен доступ до внутрішніх атрибутів у обробці подій [точна причина, по якій ми використовували жирні стрілки не тонкі стрілки].
Ну, найпростіший спосіб досягти цього - просто включити дві функції в початкове визначення класу, одну, визначену тонкою стрілкою, яка виконує операції, яку ви хочете виконати, а іншу, визначену жировою стрілкою, яка не робить нічого, крім виклику першої функції наприклад:
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
Тож коли використовувати тонкі / жирні стрілки, можна підсумувати досить легко чотирма способами:
Якщо виконані обидва умови, тонкі функції стрілки повинні використовуватися:
Функції, призначені лише для жирової стрілки, повинні використовуватися, коли виконується наступна умова:
Функція жирної стрілки, яка безпосередньо викликає функцію тонкої стрілки, повинна використовуватися, коли виконуються наступні умови:
Функція тонкої стрілки, яка безпосередньо викликає жирову стрілку (не демонструється), повинна використовуватися, коли виконуються наступні умови:
У всіх підходах це слід враховувати у випадку, коли функції прототипу можуть бути змінені, чи поведінка для конкретних екземплярів буде вести себе правильно, наприклад, хоча функція визначена жирною стрілкою, її поведінка може не бути узгодженою в екземплярі, якщо вона викликає метод, який змінюється в межах прототипу
Зазвичай, ->
це добре.
class Foo
@static: -> this
instance: -> this
alert Foo.static() == Foo # true
obj = new Foo()
alert obj.instance() == obj # true
Зауважте, як статичний метод повертає об’єкт класу для, this
а екземпляр повертає об’єкт екземпляра this
.
Що відбувається, це те, що синтаксис виклику надає значення this
. У цьому коді:
foo.bar()
foo
буде контекстом bar()
функції за замовчуванням. Тож просто сорта працює як хочеш. Стрілка жиру вам потрібна лише тоді, коли ви викликаєте цю функцію іншим способом, який не використовує синтаксис крапки для виклику.
# 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()
В обох цих випадках використання жирової стрілки для оголошення цієї функції дозволило б їм працювати. Але якщо ви робите щось дивне, зазвичай цього не потрібно.
Тому використовуйте, ->
поки вам справді не потрібно, =>
і ніколи не використовуйте =>
за замовчуванням.
x = obj.instance; alert x() == obj # false!
=>
буде метод статичних / екземплярів методів класу.
// is not a CoffeeScript comment
тоді як # is a CoffeeScript comment
.
setTimeout foo.bar, 1000
"робити це неправильно"? Використовувати жирову стрілку набагато приємніше, ніж використовувати setTimeout (-> foo.bar()), 1000
ІМХО.
setTimeout
. Але ваш перший коментар дещо надуманий і не виявляє законного випадку використання, а просто розкриває, як він може зламатися. Я просто кажу, що ви не повинні використовувати a, =>
якщо вам це не знадобиться з поважних причин, особливо це стосується методів екземплярів класу, де це має продуктивність на створення нової функції, яка повинна бути пов'язана з інстанцією.
лише приклад для відкручування жирної стрілки
не працює: (@canvas undefined)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', ->
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight
працює: (визначено @canvas)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', =>
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight