เมื่อสร้างคลาสใน CoffeeScript ควรกำหนดวิธีการอินสแตนซ์ทั้งหมดโดยใช้ตัวดำเนินการ=>
("fat arrow") และวิธีการแบบคงที่ทั้งหมดที่กำหนดโดยใช้ตัว->
ดำเนินการหรือไม่
เมื่อสร้างคลาสใน CoffeeScript ควรกำหนดวิธีการอินสแตนซ์ทั้งหมดโดยใช้ตัวดำเนินการ=>
("fat arrow") และวิธีการแบบคงที่ทั้งหมดที่กำหนดโดยใช้ตัว->
ดำเนินการหรือไม่
คำตอบ:
ไม่นั่นไม่ใช่กฎที่ฉันจะใช้
กรณีการใช้งานที่สำคัญที่ฉันพบสำหรับ 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
ในกรณีนี้ฟังก์ชั่นจะทำในสิ่งที่คาดหวังและดูเหมือนว่าจะไม่มีการสูญเสียไปกับการใช้ fat arrow แต่จะเกิดอะไรขึ้นเมื่อเราปรับเปลี่ยนต้นแบบ 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 ถูกเขียนทับอย่างถูกต้อง แต่ other_function ยังคงเหมือนเดิมในอินสแตนซ์เนื่องจากลูกศร fat ทำให้ 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
ดังนั้นเมื่อใดที่จะใช้ลูกศรผอม / อ้วนสามารถสรุปได้ค่อนข้างง่ายในสี่วิธี:
ควรใช้ฟังก์ชัน Thin arrow alone เมื่อตรงตามเงื่อนไขทั้งสอง:
ควรใช้ฟังก์ชัน Fat arrow alone เมื่อตรงตามเงื่อนไขต่อไปนี้:
ควรใช้ฟังก์ชันลูกศรไขมันซึ่งเรียกใช้ฟังก์ชันลูกศรผอมโดยตรงเมื่อตรงตามเงื่อนไขต่อไปนี้:
ควรใช้ฟังก์ชันลูกศรแบบบางซึ่งเรียกฟังก์ชันลูกศรไขมันโดยตรง (ไม่แสดงให้เห็น) เมื่อตรงตามเงื่อนไขต่อไปนี้:
ในทุกแนวทางจะต้องพิจารณาในกรณีที่ฟังก์ชันต้นแบบอาจมีการเปลี่ยนแปลงว่าพฤติกรรมสำหรับอินสแตนซ์เฉพาะจะทำงานได้อย่างถูกต้องหรือไม่เช่นแม้ว่าฟังก์ชันจะถูกกำหนดด้วยลูกศรไขมันพฤติกรรมของมันอาจไม่สอดคล้องกันภายในอินสแตนซ์หากเรียกใช้ วิธีการที่เปลี่ยนแปลงภายในต้นแบบ
โดยปกติ->
เป็นเรื่องปกติ
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()
ฟังก์ชันโดยค่าเริ่มต้น ดังนั้นเพียงแค่ sorta ทำงานตามที่คุณต้องการ คุณต้องใช้ลูกศรอ้วนเมื่อคุณเรียกใช้ฟังก์ชันเหล่านี้ด้วยวิธีอื่นที่ไม่ใช้ไวยากรณ์จุดสำหรับการเรียกใช้
# 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
"ทำมันผิด"? การใช้ fat-arrow นั้นดีกว่าการใช้setTimeout (-> foo.bar()), 1000
IMHO มาก
setTimeout
นั้นแน่นอน แต่ความคิดเห็นแรกของคุณค่อนข้างมีการจัดทำขึ้นและไม่ได้เปิดเผยกรณีการใช้งานที่ถูกต้อง แต่เพียงแค่เปิดเผยว่ามันอาจแตกได้อย่างไร ฉันแค่บอกว่าคุณไม่ควรใช้ a =>
เว้นแต่คุณจะต้องการมันด้วยเหตุผลที่ดีโดยเฉพาะอย่างยิ่งในวิธีการอินสแตนซ์คลาสที่มีต้นทุนด้านประสิทธิภาพในการสร้างฟังก์ชันใหม่ที่จำเป็นต้องเชื่อมโยงกับการสร้างอินสแตนซ์
เป็นเพียงตัวอย่างสำหรับการไม่เข้าใจลูกศรอ้วน
ไม่ทำงาน: (@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