CoffeeScript에서 클래스를 작성할 때 =>
( "fat arrow") 연산자를 사용하여 모든 인스턴스 메소드를 정의해야 하고 ->
연산자를 사용하여 정의 된 모든 정적 메소드를 정의 해야합니까?
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
다른 인스턴스 메소드 와 같이 인스턴스의 필드에 액세스 하거나 호출 할 수 없습니다 . 마지막 행은가는 화살표가 사용 된 경우에 대한 해결 방법입니다.
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
앞에서 정의한 프로토 타입 함수를 재정의하면 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
따라서 얇고 뚱뚱한 화살표를 사용할 때 다음 네 가지 방법으로 상당히 쉽게 요약 할 수 있습니다.
두 조건이 모두 충족 될 때 얇은 화살표 만 사용되어야합니다.
다음 조건이 충족 될 때 팻 화살표 만 기능을 사용해야합니다.
다음 조건이 충족되면 얇은 화살표 기능을 직접 호출하는 팻 화살표 기능을 사용해야합니다.
다음 조건이 충족 될 때 팻 화살표 (미도시) 기능을 직접 호출하는 얇은 화살표 기능을 사용해야합니다.
모든 접근법에서 특정 인스턴스에 대한 동작이 올바르게 작동하는지 여부와 같이 프로토 타입 함수가 변경 될 수있는 경우에 고려해야합니다. 예를 들어 함수가 굵은 화살표로 정의되어 있지만 호출하면 해당 동작이 인스턴스 내에서 일관성이 없을 수 있습니다 프로토 타입 내에서 변경된 방법
보통 ->
은 괜찮습니다.
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
IMHO를 사용하는 것보다 훨씬 좋습니다 .
setTimeout
물론, 그 구문에 대한 경우 가 있습니다. 그러나 첫 번째 의견은 다소 고안된 것이며 합법적 인 사용 사례는 나타내지 않지만 어떻게 중단 될 수 있는지를 보여줍니다. 나는 단순히 =>
인스턴스화에 바인딩 해야하는 새로운 함수를 생성하는 성능 비용이있는 클래스 인스턴스 메소드에서 특별한 이유로 필요하지 않으면 사용 하지 말아야한다고 말하고 있습니다.
뚱뚱한 화살표를 이해하지 못하는 예
작동하지 않습니다 : (@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