CoffeeScript, Когда использовать стрелку жира (=>) над стрелкой (->) и наоборот

При создании класса в CoffeeScript, должен ли весь метод экземпляра быть определен с помощью оператора => ( "толстая стрелка" ) и всех статических методов, определяемых с помощью оператора ->?

117
задан Ali Salehi 23 янв. '12 в 2:53
источник поделиться

4 ответов

Нет, это не правило, которое я бы использовал.

Основной прецедент, который я нашел для указателя жира в определении методов, - это когда вы хотите использовать метод в качестве обратного вызова и этот метод ссылается на экземпляры полей экземпляра:

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, или вызвать другие методы экземпляра. В последней строке есть временное решение для случаев, когда используется тонкая стрелка.

139
ответ дан nicolaskruchten 23 янв. '12 в 3:18
источник поделиться

Точка, не упомянутая в других ответах, важно отметить, что функции привязки с жирной стрелкой, когда это не обязательно, могут привести к непредвиденным результатам, таким как в этом примере, с классом, который мы просто назовем 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 после того, как он уже определен (например, сменив какое-то предупреждение или изменив вывод 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

Как мы видим, переопределение нашей ранее определенной функции прототипа заставляет some_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

Итак, когда использовать тонкие/жирные стрелки можно суммировать довольно легко четырьмя способами:

  • Функции тонкой стрелки должны использоваться, когда оба условия mett:

    • Метод никогда не будет передаваться по ссылке, включая event_handlers, например. у вас никогда не было случая, например: some_reference = some_instance.some_method; some_reference()
    • И метод должен быть универсальным по всем экземплярам, ​​поэтому, если функция прототипа изменяется, так и метод по всем экземплярам
  • Функции автономной жирной стрелки должны использоваться, когда выполняется следующее условие:

    • Метод должен быть строго привязан к экземпляру при создании экземпляра и оставаться постоянно связанным, даже если определение функции изменяется для прототипа, включая все случаи, когда функция должна быть обработчиком событий, а поведение обработчика событий должно быть последовательным
  • Функция Fat arrow, которая непосредственно вызывает функцию тонкой стрелки, должна использоваться, когда выполняются следующие условия:

    • Метод должен быть вызван по ссылке, такой как обработчик события
    • И функциональность может измениться в будущем, влияя на существующие экземпляры, заменив тонкую функцию стрелки
  • Функция тонкой стрелки, которая непосредственно вызывает функцию "жирная стрелка" (не показана), должна использоваться, когда выполняются следующие условия:

    • Функция жирной стрелки всегда должна быть прикреплена к экземпляру
    • НО функция тонкой стрелки может измениться (даже к новой функции, которая не использует функцию исходного жира)
    • И функция тонкой стрелки никогда не требуется передавать по ссылке

Во всех подходах это должно учитываться в случае, когда функции прототипа могут быть изменены независимо от того, будет ли поведение конкретных экземпляров вести себя правильно, например, хотя функция определена с помощью стрелки жира, ее поведение может быть несовместимым внутри экземпляра если он вызывает метод, который изменяется в прототипе

10
ответ дан Jamesernator 01 дек. '14 в 2:10
источник поделиться

Обычно -> отлично.

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 `this` too.
fn = foo.bar
fn()

В обоих случаях использование толстой стрелки для объявления этой функции позволит этим функциям работать. Но если вы делаете что-то странное, вам обычно не нужно.

Поэтому используйте ->, пока вам не понадобится => и никогда не используйте => по умолчанию.

8
ответ дан Alex Wayne 23 янв. '12 в 3:19
источник поделиться

просто пример непоколебимой стрелки жира

не работает: (@canvas undefined)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', ->
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight

works: (определяется @canvas)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', =>
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight
5
ответ дан user3896501 18 июля '15 в 23:37
источник поделиться

Другие вопросы по меткам