Ketika membangun kelas di CoffeeScript, haruskah semua metode instance didefinisikan menggunakan =>
operator ("panah gemuk") dan semua metode statis didefinisikan menggunakan ->
operator?
Ketika membangun kelas di CoffeeScript, haruskah semua metode instance didefinisikan menggunakan =>
operator ("panah gemuk") dan semua metode statis didefinisikan menggunakan ->
operator?
Jawaban:
Tidak, itu bukan aturan yang akan saya gunakan.
Kasus penggunaan utama yang saya temukan untuk panah-gemuk dalam mendefinisikan metode adalah ketika Anda ingin menggunakan metode sebagai panggilan balik dan metode itu merujuk bidang contoh:
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"
Seperti yang Anda lihat, Anda mungkin mengalami masalah dalam meneruskan referensi ke metode instance sebagai panggilan balik jika Anda tidak menggunakan panah gemuk. Ini karena panah-lemak mengikat instance objek ke this
sedangkan panah-tipis tidak, sehingga metode panah-tipis yang disebut sebagai callback seperti di atas tidak dapat mengakses bidang instance seperti @msg
atau memanggil metode instance lain. Baris terakhir ada solusi untuk kasus di mana panah tipis telah digunakan.
this
yang akan dipanggil dari panah tipis, tetapi juga variabel instan yang akan Anda dapatkan dengan panah gemuk?
this
diatur ke variabel yang ingin saya gunakan. Namun, saya juga ingin merujuk metode kelas, jadi saya ingin this
merujuk ke kelas juga. Saya hanya dapat memilih antara satu tugas this
, jadi apa cara terbaik untuk dapat menggunakan kedua variabel?
Suatu hal yang tidak disebutkan dalam jawaban lain yang penting untuk dicatat adalah bahwa fungsi pengikatan dengan panah gemuk ketika tidak perlu dapat menyebabkan hasil yang tidak diinginkan seperti dalam contoh ini dengan kelas kita hanya akan memanggil 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
Dalam hal ini fungsi melakukan persis apa yang diharapkan dan tampaknya tidak ada kerugian menggunakan panah gemuk, tetapi apa yang terjadi ketika kita memodifikasi prototipe DummyClass setelah itu sudah ditentukan (misalnya mengubah beberapa peringatan atau mengubah output dari 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
Seperti yang dapat kita lihat, mengesampingkan fungsi prototipe kita yang telah didefinisikan sebelumnya menyebabkan some_function ditimpa dengan benar tetapi fungsi other_ tetap sama pada instance karena panah lemak telah menyebabkan fungsi_fungsi dari kelas terikat ke semua instance sehingga instance tidak akan merujuk kembali ke kelas mereka untuk menemukan fungsi
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
Bahkan panah gemuk tidak akan berfungsi sebagai panah gemuk hanya menyebabkan fungsi terikat ke instance baru (yang memang memperoleh fungsi baru seperti yang diharapkan).
Namun ini mengarah ke beberapa masalah, bagaimana jika kita membutuhkan fungsi (misalnya dalam kasus beralih fungsi logging ke kotak keluaran atau sesuatu) yang akan bekerja pada semua instance yang ada (termasuk event handler) [karena itu kita tidak dapat menggunakan panah gemuk dalam definisi asli] tetapi kita masih membutuhkan akses ke atribut internal dalam event handler [alasan tepatnya kami menggunakan panah gemuk bukan panah tipis].
Nah cara paling sederhana untuk mencapai ini adalah dengan hanya memasukkan dua fungsi dalam definisi kelas asli, satu didefinisikan dengan panah tipis yang melakukan operasi yang ingin Anda jalankan, dan yang lain didefinisikan dengan panah gemuk yang tidak melakukan apa-apa selain memanggil fungsi pertama sebagai contoh:
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
Jadi kapan menggunakan panah tipis / gemuk dapat diringkas dengan cukup mudah dalam empat cara:
Fungsi panah tipis saja harus digunakan ketika kedua kondisi terpenuhi:
Fungsi panah gemuk saja harus digunakan ketika kondisi berikut terpenuhi:
Fungsi panah gemuk yang secara langsung memanggil fungsi panah tipis harus digunakan ketika kondisi berikut terpenuhi:
Fungsi panah tipis yang langsung memanggil fungsi panah gemuk (tidak diperlihatkan) harus digunakan ketika kondisi berikut dipenuhi:
Dalam semua pendekatan itu harus dipertimbangkan dalam kasus di mana fungsi prototipe dapat diubah apakah perilaku untuk kasus tertentu akan berperilaku dengan benar misalnya misalnya meskipun fungsi didefinisikan dengan panah gemuk, perilakunya mungkin tidak konsisten dalam sebuah instance jika ia memanggil sebuah metode yang diubah dalam prototipe
Biasanya ->
baik-baik saja.
class Foo
@static: -> this
instance: -> this
alert Foo.static() == Foo # true
obj = new Foo()
alert obj.instance() == obj # true
Perhatikan bagaimana metode statis mengembalikan objek kelas untuk this
dan instance mengembalikan objek instance untuk this
.
Apa yang terjadi adalah bahwa sintaks doa memberikan nilai this
. Dalam kode ini:
foo.bar()
foo
akan menjadi konteks bar()
fungsi secara default. Jadi itu hanya bekerja sesukamu. Anda hanya perlu panah gemuk ketika Anda memanggil fungsi ini dengan cara lain yang tidak menggunakan sintaks dot untuk doa.
# 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()
Dalam kedua kasus tersebut, menggunakan panah gemuk untuk menyatakan bahwa fungsi akan memungkinkan mereka untuk bekerja. Tetapi kecuali jika Anda melakukan sesuatu yang aneh, Anda biasanya tidak perlu melakukannya.
Jadi gunakan ->
sampai Anda benar-benar membutuhkan =>
dan tidak pernah menggunakan =>
secara default.
x = obj.instance; alert x() == obj # false!
=>
akan diperlukan pada metode statis / instance kelas.
// is not a CoffeeScript comment
sedangkan # is a CoffeeScript comment
.
setTimeout foo.bar, 1000
"salah melakukannya"? Menggunakan panah gemuk jauh lebih baik daripada menggunakan setTimeout (-> foo.bar()), 1000
IMHO.
setTimeout
, tentu saja. Tetapi komentar pertama Anda agak dibuat-buat dan tidak mengungkapkan kasus penggunaan yang sah, tetapi hanya mengungkapkan bagaimana itu bisa pecah. Saya hanya mengatakan bahwa Anda tidak boleh menggunakan a =>
kecuali jika Anda membutuhkannya untuk alasan yang baik, terutama pada metode instance kelas di mana ia memiliki biaya kinerja untuk menciptakan fungsi baru yang perlu terikat pada Instansiasi.
hanya sebuah contoh untuk panah lemak yang luar biasa
tidak berfungsi: (@canvas tidak terdefinisi)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', ->
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight
berfungsi: (didefinisikan oleh @canvas)
class Test
constructor: ->
@canvas = document.createElement 'canvas'
window.addEventListener 'resize', =>
@canvas.width = window.innerWidth
@canvas.height = window.innerHeight