कॉफीस्क्रिप्ट, जब तीर (->) के ऊपर (और>) वसा तीर का उपयोग करना है


133

कॉफ़ीस्क्रिप्ट में एक क्लास बनाते समय, सभी =>("वसा तीर") ऑपरेटर का उपयोग करके सभी इंस्टेंस विधि को परिभाषित किया जाना चाहिए और ऑपरेटर का उपयोग करते हुए सभी स्थिर तरीकों को परिभाषित किया जाना चाहिए ->?


आप कुछ नमूना कोड पोस्ट कर सकते हैं?
सरनादो

इस उत्तर को भी देखें stackoverflow.com/a/17431824/517371
तोबिया

जवाबों:


157

नहीं, यह वह नियम नहीं है जिसका मैं उपयोग करूंगा।

परिभाषित करने के तरीकों में वसा-तीर के लिए मुझे जो प्रमुख उपयोग-मामला मिला है, वह है जब आप कॉलबैक के रूप में एक विधि का उपयोग करना चाहते हैं और यह विधि उदाहरण के क्षेत्रों को संदर्भित करती है:

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या अन्य इंस्टेंस विधियों को कॉल नहीं कर सकता है । अंतिम पंक्ति उन मामलों के लिए एक समाधान है जहां पतले-तीर का उपयोग किया गया है।


2
जब आप इसका उपयोग करना चाहते हैं, तो आपको thisपतले तीर से बुलाया जाएगा, लेकिन उदाहरण चर भी जो आपको वसा तीर के साथ मिलेगा?
एंड्रयू माओ

जैसा कि मैंने कहा "अंतिम पंक्ति उन मामलों के लिए एक समाधान है जहां पतले-तीर का उपयोग किया गया है।"
निकोलसक्रुचटेन

मुझे लगता है कि आपने मेरे सवाल को गलत समझा। मान लीजिए कि कॉलबैक का डिफ़ॉल्ट दायरा thisएक चर पर सेट है जिसे मैं उपयोग करना चाहता हूं। हालाँकि, मैं एक क्लास विधि का संदर्भ भी लेना चाहता हूं, इसलिए मैं thisकक्षा को भी संदर्भित करना चाहता हूं । मैं केवल एक असाइनमेंट के बीच चयन कर सकता हूं this, इसलिए दोनों चर का उपयोग करने में सक्षम होने का सबसे अच्छा तरीका क्या है?
एंड्रयू माओ

@AndrewMao तुम शायद एक टिप्पणी में जवाब देने के बजाय इस साइट पर एक पूर्ण सवाल पोस्ट करना चाहिए :)
nicolaskruchten

यह ठीक है, सवाल यह महत्वपूर्ण नहीं है। लेकिन मैं सिर्फ यह स्पष्ट करना चाहता था कि यह वह नहीं था जिसका आप अपनी अंतिम पंक्ति के कोड में उल्लेख कर रहे थे।
एंड्रयू माओ

13

अन्य बिंदुओं में उल्लेख नहीं किया गया एक बिंदु जो नोट करना महत्वपूर्ण है कि वसा तीर के साथ बाध्यकारी कार्य जब आवश्यक नहीं है तो अनपेक्षित परिणाम हो सकते हैं जैसे कि इस उदाहरण के साथ एक वर्ग जिसे हम सिर्फ डमीक्लास कहेंगे।

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::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

जैसा कि हम प्रोटोटाइप के हमारे पहले से परिभाषित फ़ंक्शन को ओवरराइड करते हुए देख सकते हैं कि कुछ_फंक्शन ठीक से अधिलेखित होने का कारण बनता है, लेकिन अन्य_फंक्शन उदाहरणों पर समान होते हैं क्योंकि वसा तीर ने वर्ग से अन्य_फंक्शन को सभी उदाहरणों से बंधे होने के कारण उत्पन्न किया है। एक समारोह खोजने के लिए

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. जब दोनों स्थितियां पूरी हो जाएं तो पतले तीर का उपयोग किया जाना चाहिए:

    • ईवेंट_हैंडलर सहित संदर्भ द्वारा विधि कभी भी पारित नहीं की जाएगी। उदाहरण के लिए आपके पास कभी ऐसा मामला नहीं है जैसे: some_reference = some_instance.some_method; some_reference ()
    • और विधि सभी उदाहरणों पर सार्वभौमिक होनी चाहिए, इसलिए यदि प्रोटोटाइप फ़ंक्शन बदलता है तो सभी उदाहरणों पर विधि करता है
  2. निम्न स्थिति पूरी होने पर, अकेले तीर का उपयोग किया जाना चाहिए:

    • विधि को उदाहरण के निर्माण में उदाहरण के लिए सटीक रूप से बाध्य किया जाना चाहिए और स्थायी रूप से बंधे रहना चाहिए भले ही फ़ंक्शन की परिभाषा प्रोटोटाइप के लिए बदल जाए, इसमें सभी मामले शामिल हैं जहां फ़ंक्शन एक इवेंट हैंडलर होना चाहिए और इवेंट हैंडलर व्यवहार सुसंगत होना चाहिए
  3. फैट एरो फंक्शन जिसे सीधे तौर पर एक पतला एरो फंक्शन कहा जाता है, का उपयोग तब किया जाना चाहिए जब निम्न स्थितियां पूरी हों:

    • ईवेंट हैंडलर जैसे संदर्भ द्वारा विधि को बुलाया जाना आवश्यक है
    • और भविष्य में पतले तीर फ़ंक्शन को बदलकर मौजूदा उदाहरणों को प्रभावित करने में कार्यक्षमता बदल सकती है
  4. पतले तीर फ़ंक्शन जो सीधे एक वसा तीर कहते हैं (प्रदर्शन नहीं किया गया) फ़ंक्शन का उपयोग तब किया जाना चाहिए जब निम्न स्थितियां मिलें:

    • वसा तीर फ़ंक्शन हमेशा उदाहरण के लिए संलग्न होना चाहिए
    • लेकिन पतले तीर फ़ंक्शन को बदल सकते हैं (एक नए फ़ंक्शन के लिए भी जो मूल वसा तीर फ़ंक्शन का उपयोग नहीं करता है)
    • और पतले तीर फ़ंक्शन को संदर्भ द्वारा पारित करने की आवश्यकता नहीं है

सभी दृष्टिकोणों में इस मामले में विचार किया जाना चाहिए जहां प्रोटोटाइप कार्यों को बदला जा सकता है या नहीं, विशिष्ट उदाहरणों के लिए व्यवहार सही तरीके से व्यवहार करेगा उदाहरण के लिए हालांकि एक फ़ंक्शन को एक वसा तीर के साथ परिभाषित किया गया है, इसका व्यवहार उदाहरण के अनुरूप नहीं हो सकता है अगर यह कॉल करता है एक तरीका जिसे प्रोटोटाइप के भीतर बदल दिया जाता है


9

आमतौर पर, ->ठीक है।

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

उन दोनों मामलों में, उस फ़ंक्शन को घोषित करने के लिए वसा तीर का उपयोग करने से उन लोगों को काम करने की अनुमति मिलेगी। लेकिन जब तक आप कुछ अजीब नहीं कर रहे हैं, आपको आमतौर पर करने की आवश्यकता नहीं है।

इसलिए ->तब तक उपयोग करें जब तक आपको वास्तव में जरूरत न हो =>और कभी भी =>डिफ़ॉल्ट रूप से उपयोग न करें ।


1
यदि आप ऐसा करते हैं तो यह विफल हो जाएगा:x = obj.instance; alert x() == obj # false!
निकोलसक्रुचेन

2
बेशक, यह होगा, लेकिन यह "गलत कर" के तहत होगा। मैंने अब अपना उत्तर संपादित कर दिया है और समझाता हूं =>कि किसी वर्ग के स्थिर / उदाहरण के तरीकों की आवश्यकता कब होगी।
एलेक्स वेन

नाइटपिक: // is not a CoffeeScript commentजबकि # is a CoffeeScript comment
nicolaskruchten

setTimeout foo.bar, 1000"यह गलत कैसे हो रहा है"? setTimeout (-> foo.bar()), 1000IMHO का उपयोग करने की तुलना में वसा-तीर का उपयोग करना बहुत अच्छा है ।
निकोलसक्रुचटेन

1
@nicolaskruchten setTimeoutनिश्चित रूप से उस वाक्य रचना के लिए एक मामला है । लेकिन आपकी पहली टिप्पणी कुछ विवादित है और एक वैध उपयोग के मामले को उजागर नहीं करती है, लेकिन केवल यह बताती है कि यह कैसे टूट सकता है। मैं बस कह रहा हूं कि आपको =>तब तक उपयोग नहीं करना चाहिए जब तक कि आपको एक अच्छे कारण के लिए इसकी आवश्यकता न हो, विशेष रूप से वर्ग उदाहरण के तरीकों पर जहां एक नया फ़ंक्शन बनाने की एक प्रदर्शन लागत होती है जो तात्कालिकता पर बाध्य होने की आवश्यकता होती है।
एलेक्स वेन

5

वसा तीर को समझने के लिए सिर्फ एक उदाहरण

काम नहीं करता है: (@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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.