CoffeeScript, Yağ oku (=>) ok (->) ve ne zaman kullanılır?


Yanıtlar:


157

Hayır, kullanacağım kural bu değil.

Yöntemleri tanımlarken fat-arrow için bulduğum en büyük kullanım örneği, bir yöntemi geri arama olarak kullanmak istediğinizde ve bu yöntem örnek alanlarına başvurduğunda:

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"

Gördüğünüz gibi, yağ-okunu kullanmıyorsanız, bir örneğin yöntemine bir geri arama olarak bir referans iletmekte sorun yaşayabilirsiniz. Bunun nedeni, yağ-okunun nesnenin örneğini bağlamasıdır, thisancak ince-ok, yukarıdaki gibi geri çağırma olarak adlandırılan ince ok yöntemleri, örneğin alanlarına erişemez @msgveya başka örnek yöntemleri çağıramaz. Son satır, ince okun kullanıldığı durumlar için bir geçici çözüm vardır.


2
thisİnce oktan çağrılacak olanı kullanmak istediğinizde , aynı zamanda şişman okla alacağınız örnek değişkenleri ne yapmak istiyorsunuz?
Andrew Mao

Dediğim gibi "Son satır, ince okun kullanıldığı durumlar için bir çözüm var."
nicolaskruchten

Sanırım sorumu yanlış anladın. Varsayılan geri aramanın kapsamının thiskullanmak istediğim bir değişkene ayarlandığını varsayalım . Ancak, ben de bir sınıf yöntemi thisbaşvurmak istiyorum, bu yüzden de sınıfa başvurmak istiyorum . Ben sadece bir ödev arasında seçim yapabilirsiniz this, bu yüzden her iki değişken kullanmak için en iyi yolu nedir?
Andrew Mao

@AndrewMao bana bir yorumda cevap vermek yerine muhtemelen bu sitede tam bir soru göndermelisiniz :)
nicolaskruchten

Sorun yok, soru o kadar önemli değil. Ama sadece son kod satırında bahsettiğiniz şey olmadığını açıklığa kavuşturmak istedim.
Andrew Mao

13

Dikkat edilmesi gereken diğer cevaplarda belirtilmeyen bir nokta, gerekli olmadığında yağ okuyla bağlanma işlevlerinin, bu örnekte sadece DummyClass olarak adlandıracağımız bir sınıf gibi istenmeyen sonuçlara yol açabileceğidir.

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

Bu durumda işlevler tam olarak ne beklediğini yapar ve yağ okunu kullanmanın hiç bir kaybı yoktur, ancak DummyClass prototipini önceden tanımlandıktan sonra değiştirdiğimizde ne olur (örneğin, bazı uyarıları değiştirme veya bir günlük çıktısını değiştirme) :

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

Prototipin daha önce tanımlanmış fonksiyonumuzu geçersiz kıldığını görebileceğimiz gibi, some_function'un üzerine yazılmasına neden oluyor, ancak other_function, yağ okunun sınıftaki other_function'un tüm örneklere bağlanmasına neden olduğu durumlarda aynı kalıyor, böylece örnekler sınıflarına geri dönmeyecek bir işlev bulmak

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

Şişman ok bile şişman ok olarak işlev görmez, sadece işlevin yeni örneklere bağlanmasına neden olur (beklendiği gibi yeni işlevleri kazanır).

Ancak bu bazı sorunlara yol açar, ya var olan tüm örneklerde (olay işleyicileri dahil) çalışacak bir işleve (örneğin bir günlük tutma işlevini bir çıkış kutusuna veya başka bir şeye geçirme durumunda) ihtiyacımız olursa [kullanamayız orijinal tanımdaki yağ okları], ancak yine de bir olay işleyicideki dahili niteliklere erişmemiz gerekiyor [ince okları değil yağ oklarını kullanmamızın kesin nedeni].

Bunu başarmanın en basit yolu, sadece orijinal sınıf tanımına iki işlevi dahil etmektir, biri yürütmek istediğiniz işlemleri yapan ince bir okla tanımlanmış, diğeri ise ilk işlevi çağırmaktan başka bir şey yapmayan bir yağ oku ile tanımlanmıştır. Örneğin:

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

Bu nedenle, ince / şişman okları ne zaman kullanacağınız dört şekilde oldukça kolay bir şekilde özetlenebilir:

  1. Her iki koşul da sağlandığında yalnızca ince ok işlevleri kullanılmalıdır:

    • Yöntem, event_handlers dahil olmak üzere hiçbir zaman referans ile geçilmeyecektir; some_reference ()
    • AND yöntemi tüm örneklerde evrensel olmalıdır, bu nedenle prototip işlevi değişirse yöntem tüm örneklerde değişir
  2. Aşağıdaki koşul gerçekleştiğinde yalnızca yağ oku işlevleri kullanılmalıdır:

    • Yöntem, örnek oluşturmadaki örneğe tam olarak bağlanmalı ve prototip için işlev tanımı değişse bile kalıcı olarak bağlı kalmalıdır; bu, işlevin bir olay işleyicisi olması ve olay işleyicisi davranışının tutarlı olması gereken tüm durumları içerir.
  3. Aşağıdaki koşullar gerçekleştiğinde doğrudan ince ok işlevini çağıran yağ ok işlevi kullanılmalıdır:

    • Yöntemin, olay işleyici gibi başvuru ile çağrılması gerekir
    • VE işlevsellik gelecekte ince ok işlevini değiştirerek mevcut örnekleri etkileyerek değişebilir
  4. Aşağıdaki koşullar gerçekleştiğinde doğrudan bir yağ oku (gösterilmemiştir) işlevini çağıran ince ok işlevi kullanılmalıdır:

    • Yağ oku işlevi her zaman örneğe eklenmelidir
    • ANCAK ince ok işlevi değişebilir (orijinal yağ ok işlevini kullanmayan yeni bir işlevde bile)
    • VE ince ok fonksiyonunun referans ile geçilmesine asla gerek yoktur

Tüm yaklaşımlarda, belirli örneklerin davranışının doğru davranıp davranmayacağının örneğin prototip işlevlerinin değiştirilebileceği durumda, örneğin bir işlev bir yağ oku ile tanımlanmış olmasına rağmen, çağrıldığında davranışı bir örnek içinde tutarlı olmayabilir. prototip içinde değiştirilen bir yöntem


9

Genellikle ->iyidir.

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

alert Foo.static() == Foo # true

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

Statik yöntemin sınıf nesnesini nasıl thisdöndürdüğünü ve örneğin, örnek nesnesini nasıl döndürdüğünü unutmayın this.

Olan şey, çağırma sözdiziminin değerini sağlamasıdır this. Bu kodda:

foo.bar()

foobar()varsayılan olarak işlevin bağlamı olacaktır . Yani sadece sorta senin istediğin gibi çalışıyor. Yağ okuna yalnızca bu işlevi çağırmak için nokta sözdizimini kullanmayan başka bir şekilde çağırdığınızda ihtiyacınız vardır.

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

Her iki durumda da, bu işlevi ilan etmek için şişman bir ok kullanılması, bunların çalışmasına izin verecektir. Ama garip bir şey yapmazsan, genellikle yapmana gerek yok.

Bu yüzden ->gerçekten ihtiyacınız olana kadar kullanın =>ve asla =>varsayılan olarak kullanmayın .


1
Bunu yaparsanız başarısız olur:x = obj.instance; alert x() == obj # false!
nicolaskruchten

2
Tabii ki olacak, ama bu "yanlış yapmak" altına düşecekti. Şimdi cevabımı düzenledim ve =>bir sınıfın statik / örnek yöntemlerinde ne zaman gerekli olacağını açıkladım.
Alex Wayne

Nitpick: // is not a CoffeeScript commentoysa # is a CoffeeScript comment.
nicolaskruchten

setTimeout foo.bar, 1000"Yanlış yapmak" nasıl ? Bir yağ oku kullanmak setTimeout (-> foo.bar()), 1000IMHO kullanmaktan çok daha hoş .
nicolaskruchten

1
@nicolaskruchten Bu sözdizimi için setTimeoutelbette bir durum var . Ancak ilk yorumunuz biraz çelişkili ve meşru bir kullanım durumu ortaya koymuyor, ancak nasıl kırılacağını ortaya koyuyor. Basitçe =>, iyi bir nedenden dolayı gerekmedikçe, özellikle örneklemeye bağlı olması gereken yeni bir işlev oluşturma performans maliyetine sahip olduğu sınıf örneği yöntemlerinde kullanmamanız gerektiğini söylüyorum .
Alex Wayne

5

sadece yağ okunu anlama için bir örnek

çalışmıyor: (@canvas undefined)

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

eserleri: (@canvas tanımlandı)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', =>
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.