Kodokon kodokon.com

फ़ंक्शनल पैटर्न: करींग, कंपोज़िशन, इम्यूटेबिलिटी

करींग, फ़ंक्शन कंपोज़िशन और इम्यूटेबल डेटा के साथ अपने कोड को संरचित करें ताकि पूर्वानुमेयता और परीक्षण-योग्यता प्राप्त हो।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

JavaScript फ़ंक्शनों को प्रथम-श्रेणी मान के रूप में मानता है, जो फ़ंक्शनल पैटर्न के लिए द्वार खोलता है। करींग n तर्कों वाले किसी फ़ंक्शन को एकल-तर्क फ़ंक्शनों की एक श्रृंखला में बदल देता है: f(a, b, c) f(a)(b)(c) बन जाता है। इसे आंशिक अनुप्रयोग (partial application) के साथ भ्रमित न करें, जो एक ही चरण में कुछ तर्कों को स्थिर कर देता है (fn.bind(null, a))। करींग सामान्य फ़ंक्शनों से विशेषीकृत फ़ंक्शन बनाने में चमकता है।

JAVASCRIPT
const curry = (fn) => {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) return fn(...args);
    return (...rest) => curried(...args, ...rest);
  };
};

const add3 = (a, b, c) => a + b + c;
const addCurried = curry(add3);

console.log(addCurried(1)(2)(3)); // 6
console.log(addCurried(1, 2)(3)); // 6
फ़ंक्शन की arity पर आधारित एक सामान्य curry।

एक स्पेक सूक्ष्मता: fn.length केवल उन पैरामीटरों को गिनता है जो पहले डिफ़ॉल्ट या rest पैरामीटर से पहले स्थित हैं। (a, b = 1) => {} की length 1 है, (...args) => {} की 0। इसलिए arity-आधारित curry को सरल पैरामीटरों वाले फ़ंक्शन दिए जाने चाहिए। कंपोज़िशन अगला कदम है: छोटे शुद्ध फ़ंक्शनों को पठनीय पाइपलाइनों में जोड़ना।

JAVASCRIPT
const pipe = (...fns) => (input) =>
  fns.reduce((acc, fn) => fn(acc), input);

const trim = (s) => s.trim();
const lower = (s) => s.toLowerCase();
const dashes = (s) => s.replace(/\s+/g, "-");

const toSlug = pipe(trim, lower, dashes);
console.log(toSlug("  Hello World  "));
// "hello-world"
pipe फ़ंक्शनों को बाएँ से दाएँ लागू करता है।

pipe(f, g, h)(x) h(g(f(x))) की गणना करता है: आप इसे निष्पादन क्रम में पढ़ते हैं। compose संस्करण गणितीय संकेतन की तरह दाएँ से बाएँ लागू होता है। ये पाइपलाइनें केवल तभी मूल्यवान हैं जब फ़ंक्शन शुद्ध हों - कोई साइड इफ़ेक्ट नहीं, कोई छिपी हुई निर्भरता नहीं - जो इम्यूटेबल डेटा की माँग करता है: किसी वस्तु को म्यूटेट करने के बजाय, आप उसका एक नया संस्करण बनाते हैं, और तब किसी परिवर्तन का पता लगाने के लिए संदर्भ समानता ही पर्याप्त होती है (React के reconciliation का हृदय)।

JAVASCRIPT
const state = Object.freeze({
  user: { name: "Ada" },
  tags: ["js"]
});

const next = {
  ...state,
  tags: [...state.tags, "fp"]
};

const deepCopy = structuredClone(state);
deepCopy.user.name = "Grace"; // independent copy
म्यूटेशन के बजाय कॉपी करके एक नया संस्करण।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. करींग और आंशिक अनुप्रयोग के बीच क्या अंतर है?
    • कोई नहीं, ये पर्यायवाची हैं
    • करींग f(a, b, c) को f(a)(b)(c) में बदलता है; आंशिक अनुप्रयोग एक ही कॉल में कुछ तर्कों को स्थिर करता है
    • आंशिक अनुप्रयोग केवल bind के साथ काम करता है, करींग केवल ऐरो फ़ंक्शन के साथ
    • करींग केवल दो-तर्क वाले फ़ंक्शनों पर लागू होता है
  2. pipe(f, g, h)(x) किसके समकक्ष है?
    • f(g(h(x)))
    • h(g(f(x)))
    • g(f(h(x)))
    • f(x) + g(x) + h(x)
  3. const s = Object.freeze({ a: { b: 1 } }) के बाद, क्या असाइनमेंट s.a.b = 2 काम करता है?
    • नहीं, freeze पूरे ऑब्जेक्ट ट्री को फ़्रीज़ कर देता है
    • नहीं, स्ट्रिक्ट मोड के बाहर भी एक TypeError थ्रो होता है
    • हाँ, क्योंकि freeze उथला है: नेस्टेड वस्तु s.a फ़्रीज़ नहीं है
    • हाँ, लेकिन केवल नॉन-स्ट्रिक्ट मोड में