जावास्क्रिप्ट में एकाधिक तीर फ़ंक्शंस का क्या मतलब है?


472

मैं reactकोड का एक गुच्छा पढ़ रहा हूं और मुझे इस तरह से सामान दिखाई देता है जो मुझे समझ में नहीं आता है:

handleChange = field => e => {
  e.preventDefault();
  /// Do something here
}

11
बस मनोरंजन के लिए, काइल सिम्पसन ने इस प्रवाह चार्ट में तीरों के लिए सभी निर्णय पथ डाल दिए । स्रोत:
ईएस 6

के रूप में महान जवाब और अब एक इनाम हैं। क्या आप कृपया विस्तार से बता सकते हैं कि आप क्या नहीं समझते कि नीचे दिए गए उत्तर कवर नहीं हैं।
माइकल वार्नर

5
तीर फ़ंक्शन फ़्लो चार्ट का URL अब टूट गया है क्योंकि पुस्तक का एक नया संस्करण है। कार्य URL पर है raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/...
धीरज गुप्ता

जवाबों:


831

यह एक क्यूरेटेड फंक्शन है

सबसे पहले, दो मापदंडों के साथ इस फ़ंक्शन की जाँच करें ...

const add = (x, y) => x + y
add(2, 3) //=> 5

यहाँ यह फिर से विचित्र रूप में है ...

const add = x => y => x + y

यहाँ एक ही है 1 तीर कार्यों के बिना कोड ...

const add = function (x) {
  return function (y) {
    return x + y
  }
}

ध्यान केंद्रित करना return

यह इसे दूसरे तरीके से देखने में मदद कर सकता है। हम जानते हैं कि एरो फ़ंक्शंस इस तरह काम करते हैं - चलो वापसी मूल्य पर विशेष ध्यान दें ।

const f = someParam => returnValue

इसलिए हमारे addसमारोह एक रिटर्न समारोह - हम जोड़ा स्पष्टता के लिए कोष्ठक का उपयोग कर सकते हैं। बोल्ड पाठ हमारे समारोह के रिटर्न मान हैadd

const add = x => (y => x + y)

addकुछ संख्या के दूसरे शब्दों में एक फ़ंक्शन देता है

add(2) // returns (y => 2 + y)

कॉल किए गए कार्य

तो हमारे करी समारोह का उपयोग करने के लिए, हमें इसे थोड़ा अलग तरीके से कॉल करना होगा ...

add(2)(3)  // returns 5

ऐसा इसलिए है क्योंकि पहला (बाहरी) फ़ंक्शन कॉल एक दूसरा (आंतरिक) फ़ंक्शन देता है। दूसरे फ़ंक्शन को कॉल करने के बाद ही हमें वास्तव में परिणाम मिलता है। यह अधिक स्पष्ट है अगर हम कॉल को दो लाइनों पर अलग करते हैं ...

const add2 = add(2) // returns function(y) { return 2 + y }
add2(3)             // returns 5

हमारी नई समझ को अपने कोड में लागू करना

संबंधित: "बाध्यकारी, आंशिक अनुप्रयोग और करी के बीच अंतर क्या है?"

ठीक है, अब हम समझते हैं कि यह कैसे काम करता है, आइए अपने कोड को देखें

handleChange = field => e => {
  e.preventDefault()
  /// Do something here
}

हम तीर कार्यों का उपयोग किए बिना इसका प्रतिनिधित्व करके शुरू करेंगे ...

handleChange = function(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
    // return ...
  };
};

हालाँकि, क्योंकि तीर लेक्सिक रूप से काम करता है this, यह वास्तव में इस तरह दिखेगा ...

handleChange = function(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
    // return ...
  }.bind(this)
}.bind(this)

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

1 यहाँ मुझे thisमूल रूप से बाँधने की ज़रूरत नहीं थी क्योंकि मूल addफ़ंक्शन किसी संदर्भ का उपयोग नहीं करता है, इसलिए इस मामले में इसे संरक्षित करना महत्वपूर्ण नहीं है।


और भी तीर

यदि आवश्यक हो तो दो से अधिक तीर फ़ंक्शंस का अनुक्रम किया जा सकता है -

const three = a => b => c =>
  a + b + c

const four = a => b => c => d =>
  a + b + c + d

three (1) (2) (3) // 6

four (1) (2) (3) (4) // 10

करीने वाले कार्य आश्चर्यजनक चीजों में सक्षम हैं। नीचे हम $दो मापदंडों के साथ एक क्युरेटेड फ़ंक्शन के रूप में परिभाषित देखते हैं , फिर भी कॉल साइट पर, ऐसा प्रतीत होता है जैसे हम किसी भी तर्क की आपूर्ति कर सकते हैं। Currying की अमूर्त है arity -

const $ = x => k =>
  $ (k (x))
  
const add = x => y =>
  x + y

const mult = x => y =>
  x * y
  
$ (1)           // 1
  (add (2))     // + 2 = 3
  (mult (6))    // * 6 = 18
  (console.log) // 18
  
$ (7)            // 7
  (add (1))      // + 1 = 8
  (mult (8))     // * 8 = 64
  (mult (2))     // * 2 = 128
  (mult (2))     // * 2 = 256
  (console.log)  // 256

आंशिक आवेदन

आंशिक अनुप्रयोग एक संबंधित अवधारणा है। यह हमें कार्य को आंशिक रूप से लागू करने की अनुमति देता है, करी के समान, सिवाय फ़ंक्शन को करी रूप में परिभाषित किए जाने की आवश्यकता नहीं है -

const partial = (f, ...a) => (...b) =>
  f (...a, ...b)

const add3 = (x, y, z) =>
  x + y + z

partial (add3) (1, 2, 3)   // 6

partial (add3, 1) (2, 3)   // 6

partial (add3, 1, 2) (3)   // 6

partial (add3, 1, 2, 3) () // 6

partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3

यहाँ partialआप अपने ब्राउज़र में साथ काम कर सकते हैं -

const partial = (f, ...a) => (...b) =>
  f (...a, ...b)
  
const preventDefault = (f, event) =>
  ( event .preventDefault ()
  , f (event)
  )
  
const logKeypress = event =>
  console .log (event.which)
  
document
  .querySelector ('input[name=foo]')
  .addEventListener ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">


2
यह बकाया है! हालांकि कोई वास्तव में '$' को कितनी बार असाइन करता है? या प्रतिक्रिया में यह इसके लिए एक उपनाम है? अंतिम पर मेरी अज्ञानता को क्षमा करें, सिर्फ उत्सुक क्योंकि मुझे एक प्रतीक को अन्य भाषाओं में भी अक्सर असाइनमेंट नहीं मिलता है।
19ern में Caperneoignis

7
@Caperneoignis $का उपयोग अवधारणा को प्रदर्शित करने के लिए किया गया था, लेकिन आप इसे जो चाहें नाम दे सकते हैं। संयोगवश, लेकिन पूरी तरह से असंबंधित, $ का उपयोग लोकप्रिय पुस्तकालयों में किया गया है जैसे कि jQuery, जहां $कार्यों के पूरे पुस्तकालय में वैश्विक प्रवेश बिंदु की तरह है। मुझे लगता है कि यह दूसरों में भी इस्तेमाल किया गया है। एक और आप देखेंगे _कि अंडरस्कोर और लॉश जैसे पुस्तकालयों में लोकप्रिय है। कोई भी प्रतीक दूसरे से अधिक सार्थक नहीं है; आप अपने कार्यक्रम के लिए अर्थ प्रदान करते हैं । यह केवल मान्य जावास्क्रिप्ट है: D
धन्यवाद

1
पवित्र फ्रेजोली, अच्छा जवाब। कामना स्वीकार करेंगे
1919 mtyson

2
@Blake आप $इसका बेहतर उपयोग करके देख सकते हैं कि इसका उपयोग कैसे किया जाता है। यदि आप स्वयं कार्यान्वयन के बारे में पूछ रहे हैं, $तो एक फ़ंक्शन है जो एक मान प्राप्त करता है xऔर एक नया फ़ंक्शन देता है k => ...। लौटे फ़ंक्शन के शरीर को देखते हुए, हम देखते हैं k (x)कि हम जानते हैं कि kयह भी एक फ़ंक्शन होना चाहिए, और जो भी परिणाम होता k (x)है उसे वापस डाल दिया जाता है $ (...), जिसे हम जानते हैं कि एक k => ...और रिटर्न होता है , और इस पर जाता है ... यदि आप अभी भी हैं अटक जाना, मुझे बता देना।
धन्यवाद

2
जबकि इस उत्तर में बताया गया है कि यह कैसे काम करता है और इस तकनीक के साथ क्या पैटर्न हैं। मुझे लगता है कि वास्तव में किसी भी परिदृश्य में यह एक बेहतर समाधान क्यों है, इस पर कुछ खास नहीं है। किस स्थिति में, abc(1,2,3)आदर्श से कम है abc(1)(2)(3)। कोड के तर्क के बारे में तर्क करना कठिन है और फ़ंक्शन एबीसी को पढ़ना मुश्किल है और फ़ंक्शन कॉल को पढ़ना मुश्किल है। इससे पहले कि आप केवल यह जानना चाहते हैं कि एबीसी क्या करता है, अब आप सुनिश्चित नहीं हैं कि एबीसी नामांकित कार्य क्या कर रहा है, और उस पर दो बार।
मुहम्मद उमर

57

तीर फ़ंक्शन के उपलब्ध सिंटैक्स को समझना समझने से आपको इस बात की समझ मिल जाएगी कि उपलब्ध कराए गए उदाहरणों में 'जंजीर' की तरह वे किस व्यवहार का परिचय दे रहे हैं।

जब एक तीर फ़ंक्शन को कई मापदंडों के साथ या उसके बिना ब्लॉक ब्रेसिज़ के लिखा जाता है, तो फ़ंक्शन के शरीर का गठन करने वाला अभिव्यक्ति निहित रूप से वापस आ जाता है। आपके उदाहरण में, वह अभिव्यक्ति एक और एरो फंक्शन है।

No arrow funcs              Implicitly return `e=>{…}`    Explicitly return `e=>{…}` 
---------------------------------------------------------------------------------
function (field) {         |  field => e => {            |  field => {
  return function (e) {    |                             |    return e => {
      e.preventDefault()   |    e.preventDefault()       |      e.preventDefault()
  }                        |                             |    }
}                          |  }                          |  }

तीर सिंटैक्स का उपयोग करके अनाम फ़ंक्शंस लिखने का एक और लाभ यह है कि वे उस दायरे के लिए बाध्य हैं जिसमें वे परिभाषित हैं। से MDN पर 'तीर कार्यों' :

एक तीर समारोह अभिव्यक्ति की तुलना में एक छोटे वाक्य रचना है समारोह भाव और lexically बांधता है इस मूल्य। एरो फ़ंक्शंस हमेशा गुमनाम होते हैं ।

यह आपके उदाहरण में विशेष रूप से उचित है, यह देखते हुए कि यह एक से लिया गया है आवेदन। जैसा कि @naomik द्वारा बताया गया है, प्रतिक्रिया में आप अक्सर एक घटक के सदस्य कार्यों का उपयोग करते हैं this। उदाहरण के लिए:

Unbound                     Explicitly bound            Implicitly bound 
------------------------------------------------------------------------------
function (field) {         |  function (field) {       |  field => e => {
  return function (e) {    |    return function (e) {  |    
      this.setState(...)   |      this.setState(...)   |    this.setState(...)
  }                        |    }.bind(this)           |    
}                          |  }.bind(this)             |  }

53

एक सामान्य टिप, यदि आप किसी नए जेएस सिंटैक्स से भ्रमित हो जाते हैं और यह कैसे संकलित करेगा, तो आप बेबल की जांच कर सकते हैं । उदाहरण के लिए अपने कोड को बेबिल में कॉपी करना और es2015 प्रीसेट का चयन करना इस तरह से आउटपुट देगा

handleChange = function handleChange(field) {
 return function (e) {
 e.preventDefault();
  // Do something here
   };
 };

कोलाहल


42

इसे इस तरह समझें, हर बार जब आप एक तीर देखते हैं, तो आप इसे बदल देते हैं function
function parametersतीर से पहले परिभाषित कर रहे हैं।
तो आपके उदाहरण में:

field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}

और फिर एक साथ:

function (field) { 
    return function (e) { 
        e.preventDefault(); 
    };
}

डॉक्स से :

// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
   // equivalent to:  => { return expression; }

// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression

6
शाब्दिक बाध्यता का उल्लेख करना न भूलें this
धन्यवाद

30

संक्षिप्त और सरल 🎈

यह एक ऐसा फंक्शन है जो शॉर्ट में लिखे गए दूसरे फंक्शन को वापस करता है।

const handleChange = field => e => {
  e.preventDefault()
  // Do something here
}

// is equal to 
function handleChange(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
  }
}

लोग ऐसा क्यों करते हैं

क्या आपको सामना करना पड़ा है जब आपको एक फ़ंक्शन लिखने की आवश्यकता होती है जिसे अनुकूलित किया जा सकता है? या आपको एक कॉलबैक फ़ंक्शन लिखना होगा जिसमें निश्चित पैरामीटर (तर्क) हैं, लेकिन आपको फ़ंक्शन को अधिक चर पास करने की आवश्यकता है लेकिन वैश्विक चर से बचना चाहिए? अगर आपका जवाब “ हाँ " है तो यह तरीका है कि इसे कैसे किया जाए।

उदाहरण के लिए हमारे पास buttononClick कॉलबैक है। और हमें idफ़ंक्शन को पास करने की आवश्यकता है , लेकिन onClickकेवल एक पैरामीटर को स्वीकार करता है event, हम इस तरह से अतिरिक्त पैरामीटर पारित नहीं कर सकते हैं:

const handleClick = (event, id) {
  event.preventDefault()
  // Dispatch some delete action by passing record id
}

यह काम नहीं करेगा!

इसलिए हम एक फ़ंक्शन बनाते हैं जो अन्य फ़ंक्शन को बिना किसी वैश्विक चर के अपने स्वयं के दायरे के साथ लौटाएगा, क्योंकि वैश्विक चर बुराई which हैं।

फ़ंक्शन के handleClick(props.id)}नीचे बुलाया जाएगा और एक फ़ंक्शन लौटाएगा और idइसके दायरे में होगा ! कोई फर्क नहीं पड़ता कि कितनी बार इसे दबाया जाएगा आईडी प्रभाव या एक दूसरे को नहीं बदलेंगे, वे पूरी तरह से पृथक हैं।

const handleClick = id => event {
  event.preventDefault()
  // Dispatch some delete action by passing record id
}

const Confirm = props => (
  <div>
    <h1>Are you sure to delete?</h1>
    <button onClick={handleClick(props.id)}>
      Delete
    </button>
  </div
)

2

आपके प्रश्न में उदाहरण एक का है curried functionजो उपयोग करता है arrow functionऔर एक हैimplicit return पहले तर्क के लिए है।

एरो फंक्शन को शाब्दिक रूप से बाँधते हैं यानी उनके पास अपना thisतर्क नहीं होता है, लेकिन thisएनक्लोज़िंग स्कोप से वैल्यू लेते हैं

उपरोक्त कोड के बराबर होगा

const handleChange = (field) {
  return function(e) {
     e.preventDefault();
     /// Do something here
  }.bind(this);
}.bind(this);

अपने उदाहरण के बारे में ध्यान देने वाली एक और बात यह है कि handleChangeएक कास्ट या फ़ंक्शन के रूप में परिभाषित किया गया है। संभवतः आप इसे एक वर्ग विधि के हिस्से के रूप में उपयोग कर रहे हैं और यह एक का उपयोग करता हैclass fields syntax

इसलिए बाहरी कार्य को सीधे बाँधने के बजाय, आप इसे क्लास कंस्ट्रक्टर में बाँध देंगे

class Something{
    constructor(props) {
       super(props);
       this.handleChange = this.handleChange.bind(this);
    }
    handleChange(field) {
        return function(e) {
           e.preventDefault();
           // do something
        }
    }
}

उदाहरण में एक और बात ध्यान देने योग्य और स्पष्ट रिटर्न के बीच का अंतर है।

const abc = (field) => field * 2;

ऊपर निहित प्रतिफल का उदाहरण है। यह मान फ़ील्ड को तर्क के रूप में लेता है और परिणाम देता हैfield*2 जो स्पष्ट रूप से फ़ंक्शन को लौटने के लिए निर्दिष्ट करता है

स्पष्ट वापसी के लिए आप मूल्य वापस करने की विधि स्पष्ट रूप से बताएंगे

const abc = () => { return field*2; }

तीर के कार्यों के बारे में ध्यान देने योग्य एक और बात यह है कि उनके पास अपना स्वयं का नहीं है, argumentsबल्कि विरासत में है कि माता-पिता के दायरे से भी।

उदाहरण के लिए यदि आप एक तीर फ़ंक्शन को परिभाषित करते हैं जैसे

const handleChange = () => {
   console.log(arguments) // would give an error on running since arguments in undefined
}

वैकल्पिक तीर फ़ंक्शन के रूप में आप उपयोग कर सकते हैं कि बाकी मापदंडों प्रदान करते हैं

const handleChange = (...args) => {
   console.log(args);
}

1

यह पूरी तरह से संबंधित नहीं हो सकता है, लेकिन चूंकि प्रश्न उल्लिखित प्रतिक्रिया का उपयोग करता है (और मैं इस SO थ्रेड में टकराता रहता हूं): डबल एरो फ़ंक्शन का एक महत्वपूर्ण पहलू है जिसका स्पष्ट रूप से यहां उल्लेख नहीं किया गया है। केवल 'पहला' एरो (फंक्शन) नाम दिया जाता है (और रन-टाइम द्वारा 'डिफरेंशियल'), कोई भी निम्न एरो गुमनाम होता है और हर रेंडर पर 'नए' ऑब्जेक्ट के रूप में रिएक्ट पॉइंट से व्यू काउंट होता है।

इस प्रकार डबल एरो फंक्शन किसी भी PureComponent को हर समय रेंडर करने का कारण बनेगा।

उदाहरण

आपके पास एक परिवर्तनशील हैंडलर के साथ एक मूल घटक है:

handleChange = task => event => { ... operations which uses both task and event... };

और जैसे एक रेंडर के साथ:

{ tasks.map(task => <MyTask handleChange={this.handleChange(task)}/> }

handleChange तो एक इनपुट या क्लिक पर इस्तेमाल किया। और यह सब काम करता है और बहुत अच्छा लगता है। लेकिन इसका मतलब यह है कि किसी भी परिवर्तन से माता-पिता को रेंडर करने वाला (जैसे कि पूरी तरह से असंबंधित राज्य परिवर्तन) हो जाएगा, यह आपके सभी MyTask के साथ-साथ PureCompords होते हुए भी फिर से प्रस्तुत करेगा।

इसे कई तरीकों से समाप्त किया जा सकता है जैसे कि 'सबसे बाहरी' तीर को पार करना और आप जिस वस्तु के साथ उसे खाना खिलाएंगे या एक कस्टम शोअपडेट लिखेंगे या मूल नाम पर वापस जा सकते हैं जैसे कि फ़ंक्शन नाम लिखना (और इसे मैन्युअल रूप से बांधना ...)

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