[coffeescript] CoffeeScript, 화살표 (->) 위에 지방 화살표 (=>)를 사용하는 경우와 그 반대

CoffeeScript에서 클래스를 작성할 때 =>( “fat arrow”) 연산자를 사용하여 모든 인스턴스 메소드를 정의해야 하고 ->연산자를 사용하여 정의 된 모든 정적 메소드를 정의 해야합니까?



답변

아니요, 이것이 제가 사용하는 규칙이 아닙니다.

메소드 정의에서 핵심 화살표에 대해 찾은 주요 유스 케이스는 메소드를 콜백으로 사용하고 해당 메소드가 인스턴스 필드를 참조하는 경우입니다.

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다른 인스턴스 메소드 와 같이 인스턴스의 필드에 액세스 하거나 호출 할 수 없습니다 . 마지막 행은가는 화살표가 사용 된 경우에 대한 해결 방법입니다.


답변

주목해야 할 다른 답변에서 언급되지 않은 요점은 필요하지 않을 때 팻 화살표로 함수를 바인딩하면이 예제에서 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

앞에서 정의한 프로토 타입 함수를 재정의하면 some_function을 올바르게 덮어 쓰지만, fat 화살표로 인해 클래스의 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

따라서 얇고 뚱뚱한 화살표를 사용할 때 다음 네 가지 방법으로 상당히 쉽게 요약 할 수 있습니다.

  1. 두 조건이 모두 충족 될 때 얇은 화살표 만 사용되어야합니다.

    • 메소드는 event_handlers를 포함하여 참조로 전달되지 않습니다. 예를 들어 다음과 같은 경우는 없습니다. some_reference = some_instance.some_method; some_reference ()
    • 그리고 프로토 타입 함수가 변경되면 모든 인스턴스에서 메소드가 변경되므로 모든 인스턴스에서 메소드가 보편적이어야합니다.
  2. 다음 조건이 충족 될 때 팻 화살표 만 기능을 사용해야합니다.

    • 이 메소드는 인스턴스 작성시 인스턴스에 정확하게 바인딩되어야하고 함수 정의가 프로토 타입에 대해 변경 되더라도 영구적으로 바인딩 된 상태를 유지해야합니다. 여기에는 함수가 이벤트 핸들러 여야하고 이벤트 핸들러 동작이 일관되어야하는 모든 경우가 포함됩니다.
  3. 다음 조건이 충족되면 얇은 화살표 기능을 직접 호출하는 팻 화살표 기능을 사용해야합니다.

    • 이벤트 핸들러와 같은 참조로 메소드를 호출해야합니다.
    • 얇은 화살표 기능을 대체하여 기존 인스턴스에 영향을주는 기능이 향후 변경 될 수 있습니다.
  4. 다음 조건이 충족 될 때 팻 화살표 (미도시) 기능을 직접 호출하는 얇은 화살표 기능을 사용해야합니다.

    • 팻 화살표 기능은 항상 인스턴스에 연결되어야합니다
    • 그러나가는 화살표 기능이 변경 될 수 있습니다 (원래의 지방 화살표 기능을 사용하지 않는 새로운 기능으로도)
    • 얇은 화살표 기능은 참조로 전달할 필요가 없습니다.

모든 접근법에서 특정 인스턴스에 대한 동작이 올바르게 작동하는지 여부와 같이 프로토 타입 함수가 변경 될 수있는 경우에 고려해야합니다. 예를 들어 함수가 굵은 화살표로 정의되어 있지만 호출하면 해당 동작이 인스턴스 내에서 일관성이 없을 수 있습니다 프로토 타입 내에서 변경된 방법


답변

보통 ->은 괜찮습니다.

class Foo
  @static:  -> this
  instance: -> this

alert Foo.static() == Foo # true

obj = new Foo()
alert obj.instance() == obj # true

정적 메소드가에 대한 클래스 객체를 this반환하고 인스턴스가에 대한 인스턴스 객체를 반환하는 방법에 유의하십시오 this.

일어나고있는 것은 호출 구문이의 값을 제공하고 있다는 것입니다 this. 이 코드에서 :

foo.bar()

foobar()기본적으로 함수 의 컨텍스트가 됩니다. 그래서 그것은 단지 당신이 원하는 방식으로 작동합니다. 도트 구문을 호출에 사용하지 않는 다른 방식으로 이러한 함수를 호출 할 때는 팻 화살표 만 필요합니다.

# 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()

두 경우 모두 뚱뚱한 화살표를 사용하여 해당 기능을 선언하면 작동 할 수 있습니다. 그러나 이상한 일을하지 않는 한 보통 필요하지 않습니다.

따라서 ->실제로 필요할 때까지 =>사용 =>하고 기본적으로 사용하지 마십시오 .


답변

뚱뚱한 화살표를 이해하지 못하는 예

작동하지 않습니다 : (@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


답변