मुझे ECMAScript 6 में एरो फ़ंक्शंस का उपयोग कब करना चाहिए?


406

यह सवाल उन लोगों पर निर्देशित किया गया है जिन्होंने आगामी ECMAScript 6 (हार्मनी) के संदर्भ में कोड शैली के बारे में सोचा है और जिन्होंने पहले ही भाषा के साथ काम किया है।

के साथ () => {}और function () {}हम ES6 में फ़ंक्शन लिखने के लिए दो समान तरीके प्राप्त कर रहे हैं। अन्य भाषाओं में लैम्बडा फ़ंक्शंस अक्सर अनाम होने से खुद को अलग करते हैं, लेकिन ECMAScript में कोई भी फ़ंक्शन अनाम हो सकता है। दो प्रकारों में से प्रत्येक के पास अद्वितीय उपयोग डोमेन हैं (अर्थात जब thisया तो स्पष्ट रूप से बाध्य होना चाहिए या स्पष्ट रूप से बाध्य नहीं होना चाहिए)। उन डोमेन के बीच बड़ी संख्या में मामले हैं जहां या तो नोटेशन करेगा।

ES6 में एरो फ़ंक्शंस में कम से कम दो सीमाएँ हैं:

  • निर्माण के साथ काम न करें newऔर इसका उपयोग न करेंprototype
  • thisइनिशियलाइजेशन में स्कोप के लिए निश्चित सीमा

ये दोनों सीमाएं एक तरफ, तीर का कार्य सैद्धांतिक रूप से नियमित कार्यों को लगभग कहीं भी बदल सकती हैं। व्यवहार में उनका उपयोग करने का सही तरीका क्या है? क्या एरो फ़ंक्शंस का उपयोग किया जाना चाहिए:

  • "हर जगह वे काम करते हैं", यानी हर जगह एक फ़ंक्शन को thisचर के बारे में अज्ञेय नहीं होना पड़ता है और हम एक वस्तु नहीं बना रहे हैं।
  • केवल "हर जगह उनकी ज़रूरत होती है", यानी इवेंट श्रोता, टाइमआउट, जिसे एक निश्चित दायरे में बंधने की आवश्यकता है
  • 'लघु' कार्यों के साथ लेकिन 'लंबे' कार्यों के साथ नहीं
  • केवल उन कार्यों के साथ जिनमें एक और तीर फ़ंक्शन नहीं है

मैं जो देख रहा हूं वह ईसीएमएस्क्रिप्ट के भविष्य के संस्करण में उपयुक्त फ़ंक्शन नोटेशन का चयन करने के लिए एक दिशानिर्देश है। दिशानिर्देश को स्पष्ट करने की आवश्यकता होगी, ताकि यह डेवलपर्स को एक टीम में सिखाया जा सके, और सुसंगत रहें ताकि इसे एक फ़ंक्शन नोटेशन से दूसरे में आगे और पीछे लगातार रिफैक्टिंग की आवश्यकता न हो।


33
आप Fixed this bound to scope at initialisationएक सीमा के रूप में मानते हैं?
14 अक्टूबर को 14

12
यह एक फायदा है, लेकिन यह एक सीमा भी हो सकती है यदि आप मूल संदर्भ के बाहर फ़ंक्शन का पुन: उपयोग करने की योजना बनाते हैं। उदाहरण के लिए, जब किसी फ़ंक्शन को गतिशील रूप से Object.prototyp के माध्यम से जोड़ते हैं। I लिमिटेशन ’से मेरा तात्पर्य यह है कि मूल्य को बदलना thisकुछ ऐसा है जिसे आप नियमित कार्यों के साथ कर सकते हैं लेकिन तीर के कार्यों के साथ नहीं।
lyschoening

1
ईमानदारी से मुझे लगता है कि कोडिंग स्टाइल दिशानिर्देशों की राय नहीं है। मुझे गलत मत समझो, मुझे लगता है कि वे महत्वपूर्ण हैं, लेकिन एक भी दिशानिर्देश नहीं है जो सभी के लिए उपयुक्त है।
फेलिक्स क्लिंग

मुझे नहीं लगता Fixed this bound to scope at initialisationकि एक सीमा है। : :) इस लेख पर एक नजर डालें exploringjs.com/es6/ch_arrow-functions.html
NgaNguyenDuy

3
@thefourtheye, "सीमा" यहाँ "सीमा का मतलब है क्योंकि एक गूंगा स्वचालित कोड अनुवादक नेत्रहीन एक दूसरे के साथ प्रतिस्थापित नहीं कर सकता है और यह मान सकता है कि सब कुछ अपेक्षित रूप से चलेगा"।
पचेरियर

जवाबों:


322

कुछ समय पहले हमारी टीम ने अपने सभी कोड (एक मिड-साइज़ AngularJS ऐप) को जावास्क्रिप्ट में माइग्रेट कर लिया था, जिसे Traceur Babel का उपयोग करके संकलित किया गया था । अब मैं ES6 और उसके बाद के कार्यों के लिए अंगूठे के निम्नलिखित नियम का उपयोग कर रहा हूं:

  • functionवैश्विक दायरे में और Object.prototypeसंपत्तियों के लिए उपयोग करें ।
  • classऑब्जेक्ट कंस्ट्रक्टर के लिए उपयोग करें ।
  • =>हर जगह उपयोग करें ।

लगभग हर जगह तीर फ़ंक्शन का उपयोग क्यों करें?

  1. स्कोप सुरक्षा: जब तीर फ़ंक्शंस का लगातार उपयोग किया जाता है, तो सब कुछ thisObjectरूट के समान उपयोग करने की गारंटी है । अगर एक भी मानक फ़ंक्शन कॉलबैक को एरो फ़ंक्शंस के साथ मिलाया जाता है, तो एक मौका है कि गुंजाइश गड़बड़ हो जाएगी।
  2. कॉम्पैक्टनेस: एरो फ़ंक्शंस पढ़ना और लिखना आसान है। (इस पर विचार किया जा सकता है इसलिए मैं आगे कुछ उदाहरण दूंगा)।
  3. स्पष्टता: जब लगभग सब कुछ एक तीर फ़ंक्शन होता है, तो कोई भी नियमित रूप functionसे गुंजाइश को परिभाषित करने के लिए तुरंत चिपक जाता है। एक डेवलपर हमेशा functionदेख सकता है कि क्या thisObjectहै।

वैश्विक स्कोप या मॉड्यूल स्कोप पर हमेशा नियमित कार्यों का उपयोग क्यों करें?

  1. एक फ़ंक्शन को इंगित करने के लिए जिसे एक्सेस नहीं करना चाहिए thisObject
  2. windowवस्तु (वैश्विक क्षेत्र) सबसे अच्छा स्पष्ट रूप से संबोधित किया है।
  3. कई Object.prototypeपरिभाषाएँ वैश्विक दायरे (विचार String.prototype.truncateआदि) में रहती हैं और जिन्हें आम तौर पर functionवैसे भी टाइप करना पड़ता है । functionवैश्विक स्तर पर लगातार उपयोग करने से त्रुटियों से बचने में मदद मिलती है।
  4. वैश्विक दायरे में कई कार्य पुरानी शैली की श्रेणी की परिभाषाओं के लिए ऑब्जेक्ट कंस्ट्रक्टर हैं।
  5. कार्यों को 1 नाम दिया जा सकता है । इसके दो फ़ायदे हैं: (1) विशेष रूप से अन्य फ़ंक्शन कॉल के function foo(){}बजाय const foo = () => {}- लिखने के लिए यह कम अजीब नहीं है । (2) फ़ंक्शन नाम स्टैक के निशान दिखाता है। हालांकि हर आंतरिक कॉलबैक का नाम देना थकाऊ होगा, सभी सार्वजनिक कार्यों का नामकरण शायद एक अच्छा विचार है।
  6. फ़ंक्शन की घोषणाएं लहराई जाती हैं , (मतलब उन्हें घोषित किए जाने से पहले एक्सेस किया जा सकता है), जो एक स्थिर उपयोगिता फ़ंक्शन में एक उपयोगी विशेषता है।


ऑब्जेक्ट कंस्ट्रक्टर

एक तीर फ़ंक्शन को तत्काल करने का प्रयास एक अपवाद फेंकता है:

var x = () => {};
new x(); // TypeError: x is not a constructor

तीर फ़ंक्शंस पर फ़ंक्शंस का एक प्रमुख लाभ इसलिए है कि फ़ंक्शंस ऑब्जेक्ट कंस्ट्रक्टर के रूप में दोगुना हैं:

function Person(name) {
    this.name = name;
}

हालाँकि, कार्यात्मक रूप से समान 2 ES हार्मनी ड्राफ्ट क्लास परिभाषा लगभग कॉम्पैक्ट है:

class Person {
    constructor(name) {
        this.name = name;
    }
}

मुझे उम्मीद है कि पूर्व अंकन का उपयोग अंततः हतोत्साहित किया जाएगा। ऑब्जेक्ट कंस्ट्रक्टर नोटेशन का उपयोग अभी भी कुछ साधारण अनाम ऑब्जेक्ट कारखानों के लिए किया जा सकता है जहां ऑब्जेक्ट प्रोग्रामेटिक रूप से उत्पन्न होते हैं, लेकिन बहुत अधिक के लिए नहीं।

जहां एक ऑब्जेक्ट कंस्ट्रक्टर की आवश्यकता होती है, एक को classऊपर दिखाए गए अनुसार फ़ंक्शन को परिवर्तित करने पर विचार करना चाहिए । सिंटैक्स अनाम कार्यों / कक्षाओं के साथ भी काम करता है।


तीर के कार्यों की पठनीयता

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

ECMAScript 5.1 में बहुत बदलाव आया है क्योंकि ECMAScript 5.1 ने हमें कार्यात्मक प्रदान किया है Array.forEach, Array.mapऔर इन सभी कार्यात्मक प्रोग्रामिंग विशेषताओं में हमारे पास फ़ंक्शन का उपयोग होता है जहां पहले-छोरों का उपयोग किया जाता था। अतुल्यकालिक जावास्क्रिप्ट काफी दूर ले गया है। ईएस 6 एक Promiseवस्तु को भी जहाज करेगा , जिसका अर्थ है और भी अधिक अनाम कार्य। कार्यात्मक प्रोग्रामिंग के लिए वापस नहीं जा रहा है। कार्यात्मक जावास्क्रिप्ट में, तीर फ़ंक्शंस नियमित कार्यों पर बेहतर होते हैं।

उदाहरण के लिए इसे लें (विशेष रूप से भ्रमित करने वाला) कोड 3 का टुकड़ा :

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

नियमित कार्यों के साथ कोड का एक ही टुकड़ा:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) { 
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b); 
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

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

मुझे लगता है कि सवाल यह है कि क्या तीर फ़ंक्शन या नियमित कार्यों का उपयोग करना समय के साथ कम प्रासंगिक हो जाएगा। अधिकांश फ़ंक्शन या तो क्लास मेथड बन जाएंगे, जो functionकीवर्ड के साथ दूर हो जाते हैं, या वे क्लास बन जाएंगे। के माध्यम से पैचिंग कक्षाओं के लिए उपयोग में रहेगा Object.prototype। इस बीच में मैं सुझाव देता हूं कि functionकिसी भी चीज के लिए कीवर्ड को आरक्षित करना चाहिए जो वास्तव में एक क्लास विधि या एक क्लास होना चाहिए।


टिप्पणियाँ

  1. ES6 युक्ति में नामित तीर फ़ंक्शंस को स्थगित कर दिया गया है । उन्हें अभी भी एक भविष्य का संस्करण जोड़ा जा सकता है।
  2. मसौदा विनिर्देश के अनुसार "वर्ग घोषणाएं / अभिव्यक्तियां एक रचनाकार फ़ंक्शन / प्रोटोटाइप जोड़ी को ठीक से फ़ंक्शन घोषणाओं के लिए बनाते हैं" जब तक कोई वर्ग extendकीवर्ड का उपयोग नहीं करता है । एक मामूली अंतर यह है कि वर्ग की घोषणाएं निरंतर हैं, जबकि फ़ंक्शन घोषणाएं नहीं हैं।
  3. एकल स्टेटमेंट एरो फ़ंक्शंस में ब्लॉक पर ध्यान दें: मैं एक ब्लॉक का उपयोग करना पसंद करता हूं जहाँ अकेले साइड इफेक्ट के लिए एरो फ़ंक्शन को कॉल किया जाता है (जैसे असाइनमेंट)। इस तरह यह स्पष्ट है कि रिटर्न वैल्यू को खारिज किया जा सकता है।

4
दूसरी बार functionजब आप उपयोग करना चाहेंगे , जब आप thisबाध्य नहीं होना चाहते हैं , है ना? इसके लिए मेरा सबसे सामान्य परिदृश्य ईवेंट्स हैं, जहां आप thisउस ऑब्जेक्ट (आमतौर पर DOM नोड) को संदर्भित करना चाहते हैं जो इवेंट को ट्रिगर करता है।
ब्रेट

13
मैं वास्तव में उदाहरण 3 में सोचता हूं, नियमित कार्य अधिक पठनीय हैं। यहां तक ​​कि गैर-प्रोग्रामर भी दिव्य हो सकते हैं। तीरों के साथ, आपको यह जानना होगा कि वे उस उदाहरण को समझने के लिए कैसे काम करते हैं। हो सकता है कि अधिक newlines तीर के उदाहरण में मदद करेंगे, लेकिन मुझे नहीं पता। बस मेरे 2 सेंट लेकिन तीर मुझे ऐंठन बनाते हैं (लेकिन मैंने अभी तक उनका उपयोग नहीं किया है, इसलिए मुझे जल्द ही परिवर्तित किया जा सकता है।)
स्पेंसर

3
@ स्पेंसर एक उचित बिंदु है। अपने स्वयं के अनुभव से, =>समय के साथ बेहतर दिख रहा है। मुझे संदेह है कि गैर-प्रोग्रामर दोनों उदाहरणों के बारे में बहुत अलग तरह से महसूस करेंगे। यदि आप ES2016 कोड लिख रहे हैं, तो आप सामान्य रूप से इस तीर के कई फ़ंक्शंस का उपयोग करके समाप्त नहीं होंगे। इस उदाहरण में, async / प्रतीक्षा और एक सरणी समझ का उपयोग करके, आप reduce()कॉल में केवल एक तीर फ़ंक्शन के साथ समाप्त करेंगे ।
lyschoening

3
मैं स्पेंसर से पूरी तरह सहमत हूं कि नियमित कार्य उस उदाहरण में कहीं अधिक पठनीय हैं।
jonschlinkert

2
अच्छा जवाब, thx! व्यक्तिगत रूप से मैं यथासंभव वैश्विक दायरे में तीर का उपयोग करता हूं। यह मुझे लगभग कोई 'फ़ंक्शन' नहीं छोड़ता है। मेरे लिए कोड में एक 'फंक्शन' का मतलब एक विशेष मामला है, जिसे बाहर रखने और सावधानी से विचार करने की आवश्यकता है।
कोफीफस

80

प्रस्ताव के अनुसार , तीर का उद्देश्य "पारंपरिक के कई सामान्य दर्द बिंदुओं को संबोधित करना और हल करना" है Function Expression। वे जुड़ कर मामलों सुधार करने के उद्देश्य thislexically और संक्षिप्त वाक्य रचना की पेशकश की।

तथापि,

  • कोई लगातार thisशाब्दिक रूप से बाँध नहीं सकता है
  • एरो फ़ंक्शन सिंटैक्स नाजुक और अस्पष्ट है

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

शाब्दिक अर्थ के बारे में this

this समस्याग्रस्त है:

function Book(settings) {
    this.settings = settings;
    this.pages = this.createPages();
}
Book.prototype.render = function () {
    this.pages.forEach(function (page) {
        page.draw(this.settings);
    }, this);
};

एरो फ़ंक्शंस उस समस्या को ठीक करने का इरादा रखते हैं जहां हमें thisकॉलबैक के अंदर की संपत्ति तक पहुंचने की आवश्यकता होती है । वहां पहले से ही है कि ऐसा करने के लिए कई तरीके हैं: एक आवंटित कर सकते हैं thisएक चर, उपयोग करने के लिए bind, या 3 तर्क पर उपलब्ध का उपयोग Arrayकुल तरीकों। अभी तक तीर सबसे सरल वर्कअराउंड प्रतीत होते हैं, इसलिए विधि को इस तरह से फिर से बनाया जा सकता है:

this.pages.forEach(page => page.draw(this.settings));

हालाँकि, विचार करें कि क्या कोड ने jQuery जैसी लाइब्रेरी का उपयोग किया है, जिसके तरीके thisविशेष रूप से बाँधते हैं । अब, इससे thisनिपटने के लिए दो मूल्य हैं :

Book.prototype.render = function () {
    var book = this;
    this.$pages.each(function (index) {
        var $page = $(this);
        book.draw(book.currentPage + index, $page);
    });
};

हमें गतिशील रूप से बांधने के functionलिए उपयोग करना चाहिए । हम यहां एरो फंक्शन का उपयोग नहीं कर सकते।eachthis

कई thisमूल्यों से निपटना भी भ्रामक हो सकता है, क्योंकि यह जानना मुश्किल है कि thisएक लेखक किस बारे में बात कर रहा था:

function Reader() {
    this.book.on('change', function () {
        this.reformat();
    });
}

क्या लेखक वास्तव में फोन करने का इरादा रखता था Book.prototype.reformat? या वह बांधना भूल गया this, और बुलाने का इरादा किया Reader.prototype.reformat? यदि हम हैंडलर को एरो फंक्शन में बदलते हैं, तो हम भी उसी तरह आश्चर्यचकित होंगे यदि लेखक डायनेमिक चाहता था this, फिर भी उसने एक एरो चुना क्योंकि यह एक लाइन में फिट होता है:

function Reader() {
    this.book.on('change', () => this.reformat());
}

कोई भी ऐसा कर सकता है: "क्या यह असाधारण है कि तीर कभी-कभी गलत फ़ंक्शन का उपयोग कर सकता है? शायद अगर हमें केवल गतिशील thisमूल्यों की आवश्यकता होती है, तो अधिकांश समय तीर का उपयोग करना ठीक होगा।"

लेकिन अपने आप से यह पूछें: "क्या यह कोड को डिबग करने के लिए 'इसके लायक' होगा और यह पता लगाएगा कि त्रुटि का परिणाम एक 'किनारे के मामले' द्वारा लाया गया था?" "मैं ज्यादातर समय परेशानी से बचना पसंद करूंगा, लेकिन समय का 100%।

एक बेहतर तरीका है: हमेशा उपयोग करें function(ताकि thisहमेशा गतिशील रूप से बाध्य किया जा सके), और हमेशा thisएक चर के माध्यम से संदर्भ । विविधताएं शाब्दिक हैं और कई नामों को मानती हैं। thisएक चर को असाइन करने से आपके इरादे स्पष्ट हो जाएंगे:

function Reader() {
    var reader = this;
    reader.book.on('change', function () {
        var book = this;
        book.reformat();
        reader.reformat();
    });
}

इसके अलावा, हमेशाthis एक वेरिएबल को असाइन करना (तब भी जब thisकोई एकल या कोई अन्य कार्य नहीं होता है) यह सुनिश्चित करता है कि कोड बदलने के बाद भी किसी के इरादे स्पष्ट रहें।

इसके अलावा, गतिशील thisशायद ही असाधारण है। jQuery का उपयोग 50 मिलियन से अधिक वेबसाइटों पर किया जाता है (फरवरी 2016 में इस लेखन के रूप में)। यहाँ अन्य एपीआई thisगतिशील रूप से बाध्यकारी हैं :

  • मोचा (~ 120k डाउनलोड कल) के माध्यम से अपने परीक्षणों के लिए तरीकों को उजागर करता है this
  • ग्रंट (कल 63k डाउनलोड) के माध्यम से निर्माण कार्यों के लिए तरीकों को उजागर करता है this
  • बैकबोन (~ 22k डाउनलोड कल) एक्सेस करने के तरीकों को परिभाषित करता है this
  • ईवेंट API (जैसे DOM) एक के EventTargetसाथ संदर्भित करता है this
  • प्रोटोटाइप एपीआई जो पैच या विस्तारित हैं, उदाहरण के साथ संदर्भित होते हैं this

( Http://trends.builtwith.com/javascript/jQuery और https://www.npmjs.com के माध्यम से आँकड़े ।)

आपको thisपहले से ही गतिशील बाइंडिंग की आवश्यकता है ।

एक शाब्दिक thisकभी-कभी अपेक्षित होती है, लेकिन कभी-कभी नहीं; जैसा कि एक गतिशील thisकभी-कभी अपेक्षित होता है, लेकिन कभी-कभी नहीं। शुक्र है, एक बेहतर तरीका है, जो हमेशा अपेक्षित बंधन का उत्पादन और संचार करता है।

ट्रिक सिंटैक्स के बारे में

एरो फ़ंक्शंस फ़ंक्शंस के लिए "छोटा वाक्य रचनात्मक रूप" प्रदान करने में सफल रहे। लेकिन क्या ये छोटे कार्य आपको अधिक सफल बनाएंगे?

क्या x => x * x"पढ़ने में आसान" है function (x) { return x * x; }? शायद यह है, क्योंकि यह कोड की एक एकल, छोटी लाइन का उत्पादन करने की अधिक संभावना है। डायसन के लिए पढ़ने की गति और स्क्रीन से पढ़ने की प्रभावशीलता पर लाइन की लंबाई का प्रभाव ,

एक मध्यम लाइन की लंबाई (प्रति पंक्ति 55 वर्ण) सामान्य और तेज गति से प्रभावी पढ़ने का समर्थन करने के लिए प्रकट होती है। इसने उच्चतम स्तर की समझ पैदा की। । ।

सशर्त (टर्नरी) ऑपरेटर के लिए और एकल-लाइन ifबयानों के लिए इसी तरह के औचित्य बनाए जाते हैं ।

हालाँकि, क्या आप वास्तव में प्रस्ताव में विज्ञापित सरल गणितीय कार्यों को लिख रहे हैं ? मेरे डोमेन गणितीय नहीं हैं, इसलिए मेरे सबरूटीन शायद ही कभी इतने सुंदर हैं। इसके बजाय, मैं आमतौर पर तीर के कार्यों को एक कॉलम की सीमा को तोड़ता हुआ देखता हूं, और संपादक या शैली गाइड के कारण दूसरी पंक्ति में लपेटता हूं, जो डायसन की परिभाषा से "पठनीयता" को कम करता है।

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

तीर फ़ंक्शन सिंटैक्स के साथ कई समस्याएं हैं:

const a = x =>
    doSomething(x);

const b = x =>
    doSomething(x);
    doSomethingElse(x);

ये दोनों कार्य वाक्यात्मक रूप से मान्य हैं। लेकिन doSomethingElse(x);शरीर में नहीं है b, यह सिर्फ एक खराब-इंडेंटेड, टॉप-लेवल स्टेटमेंट है।

ब्लॉक के रूप में विस्तार करते समय return, अब कोई निहित नहीं है , जिसे कोई भी बहाल करना भूल सकता है। लेकिन अभिव्यक्ति केवल एक साइड-इफ़ेक्ट उत्पन्न करने के उद्देश्य से की गई हो सकती है, इसलिए कौन जानता है कि एक स्पष्ट रूप returnसे आगे बढ़ना आवश्यक होगा?

const create = () => User.create();

const create = () => {
    let user;
    User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

const create = () => {
    let user;
    return User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

बाकी पैरामीटर के रूप में क्या किया जा सकता है, प्रसार ऑपरेटर के रूप में पार्स किया जा सकता है:

processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest

असाइनमेंट को डिफ़ॉल्ट तर्कों के साथ भ्रमित किया जा सकता है:

const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens

ब्लॉक वस्तुओं की तरह दिखते हैं:

(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object

इसका क्या मतलब है?

() => {}

क्या लेखक एक खाली ऑप्शन या एक ऐसा फ़ंक्शन बनाने का इरादा रखता है, जो एक खाली वस्तु लौटाता है? (इसे ध्यान में रखते, हम कभी भी इस प्रकार रखना चाहिए {के बाद =>? चाहिए हम अपने आप को केवल अभिव्यक्ति वाक्य रचना के लिए प्रतिबंधित? यही कारण है कि आगे तीर 'आवृत्ति कम होगी।)

=>जैसा दिखता है <=और >=:

x => 1 ? 2 : 3
x <= 1 ? 2 : 3

if (x => 1) {}
if (x >= 1) {}

एक तीर फ़ंक्शन अभिव्यक्ति को तुरंत लागू करने के लिए, एक को ()बाहर की तरफ रखना होगा , फिर ()भी अंदर की तरफ रखना वैध है और जानबूझकर हो सकता है।

(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function

हालांकि, अगर (() => doSomething()());कोई तुरंत-लागू फ़ंक्शन अभिव्यक्ति लिखने के इरादे से लिखता है, तो बस कुछ नहीं होगा।

यह तर्क देना कठिन है कि तीर के कार्य उपरोक्त सभी मामलों को ध्यान में रखते हुए "अधिक समझने योग्य" हैं। कोई भी इस वाक्यविन्यास का उपयोग करने के लिए आवश्यक सभी विशेष नियमों को सीख सकता है । यह वास्तव में इसके लायक है?

वाक्यविन्यास functionअनैच्छिक रूप से सामान्यीकृत है। functionविशेष रूप से उपयोग करने के लिए भाषा स्वयं को भ्रमित कोड लिखने से रोकती है। उन प्रक्रियाओं को लिखने के लिए जिन्हें सभी मामलों में वाक्यविन्यास समझा जाना चाहिए, मैं चुनता हूं function

एक दिशानिर्देश के बारे में

आप एक दिशानिर्देश का अनुरोध करते हैं जिसे "स्पष्ट" और "सुसंगत" होना चाहिए। एरो फ़ंक्शंस का उपयोग करने के परिणामस्वरूप अंतिम रूप से वाक्य-रचना, तार्किक रूप से अमान्य कोड, दोनों फ़ंक्शन रूपों के साथ intertwined, सार्थक और मनमाने ढंग से हो जाएंगे। इसलिए, मैं निम्नलिखित प्रदान करता हूं:

ES6 में फ़ंक्शन सूचना के लिए दिशानिर्देश:

  • हमेशा प्रक्रियाओं के साथ बनाएँ function
  • हमेशा thisएक चर के लिए असाइन करें। उपयोग न करें () => {}

5
जावास्क्रिप्ट पर एक कार्यात्मक प्रोग्रामर के दृष्टिकोण पर दिलचस्प लिखना। मुझे यकीन नहीं है कि मैं निजी चर तर्क से सहमत हूं। IMO कुछ लोगों को वास्तव में उनकी आवश्यकता होती है; जो लोग करते हैं, उन्हें भी शायद अन्य अनुबंध सुविधाओं की आवश्यकता होगी और टाइपस्क्रिप्ट जैसे भाषा एक्सटेंशन के लिए जाना होगा। मैं निश्चित रूप से selfइसके बजाय अपील देख सकता हूं । आपके बताए गए एरो फंक्शन के नुकसान भी सभी मान्य हैं, और अन्य स्टेटमेंट्स पर भी वही मानक हैं जो ब्रेसिज़ के बिना जा सकते हैं, निश्चित रूप से यहां भी लागू होते हैं; अन्यथा, मुझे लगता है कि आपके तर्क के साथ हर जगह एक ही तरह के वकील काम कर सकते हैं।
10

7
"चीजों को करने के कई तरीके होने से कार्यस्थल और भाषा समुदाय में तर्कों और असंतोष के लिए अनावश्यक वैक्टर बनते हैं। भाषा व्याकरण ने हमें खराब विकल्प बनाने की अनुमति नहीं दी है तो बेहतर होगा।" इतना सहमत हूं। अच्छा लिखो! मुझे लगता है कि तीर कार्य वास्तव में एक कदम पीछे हैं। एक अलग विषय पर, मैं चाहता हूं कि मेरे सहकर्मी जावास्क्रिप्ट की C # .प्रोटोटाइप परिभाषाओं की एक श्रृंखला में बदलने की कोशिश करना बंद कर दें। ये तो वाहियाद है। मुझे आपकी पोस्ट को गुमनाम रूप से जोड़ना चाहिए :)
स्पेन्सर

11
बहुत अच्छा लिखा है! हालांकि मैं आपके अधिकांश बिंदुओं से असहमत हूं, लेकिन विपरीत दृष्टिकोण पर विचार करना महत्वपूर्ण है।
मेराज

4
तीर कार्य नहीं बल्कि अजीब व्यवहार thisजावास्क्रिप्ट की समस्या है। अंतर्निहित रूप से बाध्य होने के बजाय, thisएक स्पष्ट तर्क के रूप में पारित किया जाना चाहिए।
बॉब

5
" हमेशा फ़ंक्शन का उपयोग करें (इसलिए यह हमेशा गतिशील रूप से बाध्य हो सकता है), और हमेशा इसे एक चर के माध्यम से संदर्भित करें। " मैं इससे ज्यादा असहमत नहीं हो सकता!

48

फंक्शन को सरल बनाने के लिए एरो फ़ंक्शंस बनाए गए थे scopeऔर thisकीवर्ड को अधिक सरल बनाकर इसे हल किया गया था। वे =>वाक्य रचना का उपयोग करते हैं, जो एक तीर की तरह दिखता है।

नोट: यह मौजूदा फ़ंक्शन को प्रतिस्थापित नहीं करता है। यदि आप हर फ़ंक्शन सिंटैक्स को एरो फ़ंक्शंस से प्रतिस्थापित करते हैं, तो यह सभी मामलों में काम नहीं करेगा।

आइए मौजूदा ES5 सिंटैक्स पर एक नज़र डालें, यदि thisकीवर्ड ऑब्जेक्ट के तरीके (एक फ़ंक्शन जो किसी ऑब्जेक्ट से संबंधित है) के अंदर थे, तो यह क्या संदर्भित करेगा?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

उपरोक्त स्निपेट एक को संदर्भित करेगा objectऔर नाम का प्रिंट आउट लेगा "RajiniKanth"। आइए नीचे दिए गए स्निपेट को देखें और देखें कि यह यहाँ क्या इंगित करेगा।

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

अब अगर thisकीवर्ड अंदर हो तो क्या होगा method’s function?

यहाँ यह इसके बाहर गिरने window objectके inner functionरूप में की तुलना में होगा scope। क्योंकि this, हमेशा इस मामले के लिए, इस मामले के मालिक के संदर्भ में - चूंकि यह अब दायरे से बाहर है - खिड़की / वैश्विक वस्तु।

जब यह किसी object'विधि' के अंदर होता है - function'स्वामी' वस्तु है। इस प्रकार यह कीवर्ड ऑब्जेक्ट के लिए बाध्य है। फिर भी जब यह किसी फ़ंक्शन के अंदर होता है, या तो अकेले या किसी अन्य विधि के भीतर, यह हमेशा window/globalऑब्जेक्ट को संदर्भित करेगा ।

var fn = function(){
  alert(this);
}

fn(); // [object Window]

अपने ES5आप में इस समस्या को हल करने के तरीके हैं , आइए इस पर ध्यान दें कि ईएस 6 तीर कार्यों में गोता लगाने से पहले इसे कैसे हल करें।

आमतौर पर आप विधि के आंतरिक कार्य के बाहर एक चर बनाते हैं। अब ‘forEach’विधि लाभ के लिए पहुँच thisहै और इस तरह object’sके गुण और उनके मान।

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

bindउस thisकीवर्ड को संलग्न करने के लिए उपयोग करना जो विधि को संदर्भित करता है method’s inner function

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   }.bind(this));
  }
};

Actor.showMovies();

अब ES6एरो फ़ंक्शन के साथ , हम lexical scopingसरल तरीके से समस्या से निपट सकते हैं ।

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Arrow functionsफ़ंक्शन स्टेटमेंट की तरह अधिक हैं, सिवाय इसके कि वे bindयह करने के लिए parent scope। यदि arrow function is in top scope, thisतर्क को संदर्भित करेगा window/global scope, जबकि एक नियमित कार्य के अंदर एक तीर फ़ंक्शन का यह तर्क उसके बाहरी फ़ंक्शन के समान होगा।

साथ arrowकार्यों thisसंलग्न के लिए बाध्य है scopeनिर्माण के समय पर और बदला नहीं जा सकता। नए ऑपरेटर, बाइंड, कॉल और आवेदन का इस पर कोई प्रभाव नहीं पड़ता है।

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

उपरोक्त उदाहरण में, हमने इस पर नियंत्रण खो दिया। हम एक चर संदर्भ का thisउपयोग करके या उपयोग करके उपरोक्त उदाहरण को हल कर सकते हैं bind। ईएस 6 के साथ, thisइसकी बाध्यता को प्रबंधित करना आसान हो जाता है lexical scoping

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

जब एरो फ़ंक्शंस के लिए नहीं

एक वस्तु शाब्दिक के अंदर।

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getNameएक तीर फ़ंक्शन के साथ परिभाषित किया गया है, लेकिन आह्वान पर यह अपरिभाषित है क्योंकि this.nameयह undefinedसंदर्भ जैसा है वैसा ही रहता है window

ऐसा इसलिए होता है क्योंकि एरो फ़ंक्शन संदर्भ को लेक्सिकली से बांधता है window object... अर्थात बाहरी क्षेत्र। निष्पादन this.nameके बराबर है window.name, जो अपरिभाषित है।

वस्तु प्रोटोटाइप

एक ही नियम लागू होता है जब एक पर तरीकों को परिभाषित करना prototype object। SayCatName विधि को परिभाषित करने के लिए एक तीर फ़ंक्शन का उपयोग करने के बजाय, जो एक गलत लाता है context window:

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

निर्माण करने वालों को आमंत्रित करना

thisएक निर्माण आह्वान में नव निर्मित वस्तु है। नई Fn () निष्पादित करते समय, का संदर्भ constructor Fnएक नई वस्तु है this instanceof Fn === true:।

this संलग्नक संदर्भ से सेटअप है, अर्थात बाहरी दायरा जो इसे नव निर्मित वस्तु को नहीं सौंपा गया है।

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

गतिशील संदर्भ के साथ कॉलबैक

एरो फ़ंक्शन contextस्टेटिक रूप से घोषणा पर बांधता है और इसे गतिशील बनाना संभव नहीं है। DOM एलीमेंट्स में इवेंट श्रोताओं को शामिल करना क्लाइंट साइड प्रोग्रामिंग में एक सामान्य कार्य है। कोई ईवेंट हैंडलर फ़ंक्शन को लक्ष्य तत्व के रूप में चलाता है।

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

thisवैश्विक संदर्भ में परिभाषित तीर फ़ंक्शन में विंडो है। जब कोई क्लिक ईवेंट होता है, तो ब्राउज़र बटन संदर्भ के साथ हैंडलर फ़ंक्शन को लागू करने की कोशिश करता है, लेकिन तीर फ़ंक्शन इसके पूर्व-परिभाषित संदर्भ को नहीं बदलता है। this.innerHTMLके बराबर है window.innerHTMLऔर कोई मतलब नहीं है।

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

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

जब उपयोगकर्ता बटन पर क्लिक करता है, तो हैंडलर फ़ंक्शन में यह बटन होता है। इस प्रकार this.innerHTML = 'Clicked button'क्लिक की स्थिति को प्रतिबिंबित करने के लिए बटन पाठ को सही ढंग से संशोधित करता है।

सन्दर्भ: https://dmitripavlutin.com/when-not-to-use-arrow-functions-in-javascript/


खैर, मुझे स्वीकार करना चाहिए कि "बीच में सबसे अच्छा झूठ" । कथन के लिए बनाया गया, वह तीर फ़ंक्शन किसी भी संभावित फ़ंक्शन उपयोग मामलों को कवर नहीं करेगा। वे वास्तव में सामान्य समस्याओं के केवल एक हिस्से को हल करने के लिए डिज़ाइन किए गए हैं। बस उन्हें पूरी तरह से स्विच एक ओवरकिल हो जाएगा।
ब्लिट्ज ऑक्ट

@DmitriPavlutin: मेरी अपडेट की गई पोस्ट, उसकी बहुत सी चीज़ों का एक संग्रह ... मुझे एक संदर्भ पोस्ट करना चाहिए।
थलाइवर

2
इस कोड को विधि के आंतरिक फ़ंक्शन के लिए विधि को संदर्भित करने के लिए बाइंड का उपयोग करने के बाद आपका कोड 'बाइंड का उपयोग करता है।' इसमें कीड़े हैं। क्या आपने अपने बाकी उदाहरणों का परीक्षण किया है?
आइजैक पाक

एक using bind to attach the this keyword that refers to the method to the method’s inner function.में सिंटैक्स त्रुटियां हैं।
कोडा चांग

होना चाहिएvar Actor = { name: 'RajiniKanth', movies: ['Kabali', 'Sivaji', 'Baba'], showMovies: function() { this.movies.forEach(function(movie){ alert(this.name + ' has acted in ' + movie); }.bind(this)) } }; Actor.showMovies();
कोडा चांग

14

एरो फ़ंक्शंस - अब तक सबसे व्यापक रूप से इस्तेमाल किए जाने वाले ES6 फ़ीचर ...

उपयोग: सभी ES5 कार्यों को निम्नलिखित परिदृश्यों को छोड़कर ES6 तीर कार्यों से प्रतिस्थापित किया जाना चाहिए:

एरो फ़ंक्शंस का उपयोग नहीं किया जाना चाहिए:

  1. जब हम फंक्शन उत्थापन चाहते हैं
    • तीर के कार्य अनाम हैं।
  2. हम उपयोग करना चाहते हैं जब this/ argumentsएक समारोह में
    • तीर कार्यों की जरूरत नहीं है के रूप में this/ argumentsअपनी खुद की, वे अपने बाहरी संदर्भ पर निर्भर करते हैं।
  3. जब हम नामित फ़ंक्शन का उपयोग करना चाहते हैं
    • तीर के कार्य अनाम हैं।
  4. जब हम एक के रूप में फ़ंक्शन का उपयोग करना चाहते हैं constructor
    • तीर के कार्यों के पास अपना नहीं है this
  5. जब हम ऑब्जेक्ट शाब्दिक रूप में फ़ंक्शन को जोड़ना चाहते हैं और इसमें ऑब्जेक्ट का उपयोग करते हैं
    • जैसे कि हम पहुँच नहीं सकते हैं this(जो स्वयं वस्तु होनी चाहिए)।

आइए हम बेहतर समझने के लिए एरो फ़ंक्शंस के कुछ वेरिएंट्स को समझते हैं:

वेरिएंट 1 : जब हम किसी फ़ंक्शन में एक से अधिक तर्क पास करना चाहते हैं और उसमें से कुछ मान लौटाते हैं।

ES5 संस्करण :

var multiply = function (a,b) {
    return a*b;
};
console.log(multiply(5,6)); //30

ES6 संस्करण :

var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30

नोट: functionकीवर्ड की आवश्यकता नहीं है। =>आवश्यक है। {}वैकल्पिक हैं, जब हम प्रदान नहीं करते हैं, तो हमें {} returnजावास्क्रिप्ट द्वारा जोड़ दिया जाता है और जब हम प्रदान करते {}हैं returnतो हमें जरूरत पड़ने पर जोड़ने की आवश्यकता होती है।

वेरिएंट 2 : जब हम किसी फ़ंक्शन को केवल एक तर्क पास करना चाहते हैं और उसमें से कुछ मान लौटाते हैं।

ES5 संस्करण :

var double = function(a) {
    return a*2;
};
console.log(double(2)); //4

ES6 संस्करण :

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); //4

नोट: केवल एक तर्क पारित करते समय हम कोष्ठक को छोड़ सकते हैं ()

वेरिएंट 3 : जब हम किसी फ़ंक्शन के लिए कोई तर्क पारित नहीं करना चाहते हैं और किसी भी मूल्य को वापस नहीं करना चाहते हैं।

ES5 संस्करण :

var sayHello = function() {
    console.log("Hello");
};
sayHello(); //Hello

ES6 संस्करण :

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow

वेरिएंट 4 : जब हम स्पष्ट रूप से तीर फ़ंक्शन से वापस लौटना चाहते हैं।

ES6 संस्करण :

var increment = x => {
  return x + 1;
};
console.log(increment(1)); //2

वेरिएंट 5 : जब हम एरो फ़ंक्शंस से कोई ऑब्जेक्ट वापस करना चाहते हैं।

ES6 संस्करण :

var returnObject = () => ({a:5});
console.log(returnObject());

नोट: हमें ऑब्जेक्ट को कोष्ठक में लपेटने की आवश्यकता है ()अन्यथा जावास्क्रिप्ट एक ब्लॉक और ऑब्जेक्ट के बीच अंतर नहीं कर सकता है।

वैरिएंट 6 : एरो फ़ंक्शंस उनके पास नहीं हैं arguments(ऑब्जेक्ट की तरह एक सरणी) वे अपने लिए बाहरी संदर्भ पर निर्भर करते हैं arguments

ES6 संस्करण :

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};    
foo(2); // 2

नोट: fooएक साथ, एक ES5 समारोह है argumentsयह है करने के लिए वस्तु की तरह सरणी और एक तर्क पारित कर दिया 2तो arguments[0]के लिए foo2 है।

abcएक ES6 तीर फ़ंक्शन है क्योंकि इसमें ऐसा नहीं है argumentsइसलिए यह इसके बजाय बाहरी संदर्भ के प्रिंट arguments[0]है foo

वेरिएंट 7 : एरो फ़ंक्शंस के पास thisअपना स्वयं का नहीं है , वे बाहरी संदर्भ पर निर्भर करते हैंthis

ES5 संस्करण :

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

नोट: कॉलबैक टाइमटाइमआउट के लिए दिया गया एक ES5 फ़ंक्शन है और इसका स्वयं का है thisजो use-strictपर्यावरण में अपरिभाषित है इसलिए इसे प्राप्त करें:

undefined: Katty

ES6 संस्करण :

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user)); 
      // this here refers to outer context
   }
};

obj6.greetUser("Katty"); //Hi, Welcome: Katty

नोट: कॉलबैक पास किया गया setTimeoutES6 एरो फंक्शन है और इसमें ऐसा नहीं है, thisइसलिए यह इसे बाहरी संदर्भ से लेता है greetUserजो thisकि है obj6इसलिए हमें आउटपुट मिलता है:

Hi, Welcome: Katty

विविध: हम newतीर के कार्यों के साथ उपयोग नहीं कर सकते । एरो फ़ंक्शंस में prototypeसंपत्ति नहीं है । हमारे पास उस समय बाध्यकारी नहीं है thisजब तीर फ़ंक्शन को इसके माध्यम से लागू किया जाता है applyया call


6

अब तक के शानदार जवाबों के अलावा, मैं एक बहुत अलग कारण प्रस्तुत करना चाहूंगा कि तीर के कार्य एक निश्चित अर्थ में मूल रूप से "साधारण" जावास्क्रिप्ट कार्यों से बेहतर हैं। चर्चा के लिए, आइए अस्थायी रूप से मान लें कि हम टाइपस्क्रिप्ट या फेसबुक के "फ़्लो" जैसे एक चेकर का उपयोग करते हैं। निम्नलिखित खिलौना मॉड्यूल पर विचार करें, जो वैध है ECMAScript 6 कोड प्लस फ्लो टाइप एनोटेशन: (मैं इस उत्तर के अंत में वास्तविक रूप से बेबिल कोड शामिल करूंगा, जो बाबेल का परिणाम देगा, इसलिए इसे वास्तव में चलाया जा सकता है।)

export class C {
  n : number;
  f1: number => number; 
  f2: number => number;

  constructor(){
    this.n = 42;
    this.f1 = (x:number) => x + this.n;
    this.f2 = function (x:number) { return  x + this.n;};
  }
}

अब देखें कि जब हम किसी भिन्न मॉड्यूल से वर्ग C का उपयोग करते हैं तो क्या होता है:

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

जैसा कि आप देख सकते हैं, टाइप करने वाला यहाँ फेल हो गया: f2 को एक नंबर लौटाना था, लेकिन उसने एक स्ट्रिंग लौटा दी!

इससे भी बदतर यह लगता है कि कोई भी बोधगम्य प्रकार का चेकर साधारण (गैर-तीर) जावास्क्रिप्ट फ़ंक्शंस को नहीं संभाल सकता है, क्योंकि f2 की "यह" f2 की तर्क सूची में नहीं होती है, इसलिए "इस" के लिए आवश्यक प्रकार संभवतः जोड़ा नहीं जा सका। f2 के लिए एनोटेशन के रूप में।

क्या यह समस्या उन लोगों को भी प्रभावित करती है जो टाइप चेकर्स का उपयोग नहीं करते हैं? मुझे ऐसा लगता है, क्योंकि जब हमारे पास कोई स्थिर प्रकार नहीं होता है, तो हम सोचते हैं कि जैसे वे वहां हैं। ("पहला पैरामीटर एक संख्या, दूसरा एक स्ट्रिंग होना चाहिए" आदि) एक छिपा हुआ "यह" -गुर्ज़ेशन जो फ़ंक्शन के शरीर में उपयोग नहीं किया जा सकता है या नहीं हो सकता है, यह हमारी मानसिक बहीखाता पद्धति को कठिन बनाता है।

यहाँ रन करने योग्य अनकैप्ड संस्करण है, जो बाबेल द्वारा निर्मित किया जाएगा:

class C {
    constructor() {
        this.n = 42;
        this.f1 = x => x + this.n;
        this.f2 = function (x) { return x + this.n; };
    }
}

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!



3

मैं अब भी इस धागे में अपने पहले उत्तर में लिखी हर बात से खड़ा हूं । हालांकि, कोड शैली पर मेरी राय तब से विकसित हुई है, इसलिए मेरे पास इस सवाल का एक नया जवाब है जो मेरे पिछले एक पर बनाता है।

शाब्दिक अर्थ के बारे में this

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

मेरा विश्वास यह है: हमें thisपहली जगह का उपयोग नहीं करना चाहिए । इसलिए, यदि कोई व्यक्ति जानबूझकर thisअपने कोड का उपयोग करने से बचता है , तो "लेक्सिकल this" तीरों की विशेषता बहुत कम है जिसका कोई मूल्य नहीं है। इसके अलावा, इस आधार के तहत कि thisएक बुरी चीज है, तीर का उपचार this"अच्छी बात" से कम है; इसके बजाय, यह एक और बुरी भाषा सुविधा के लिए क्षति नियंत्रण का एक रूप है।

मेरा मानना ​​है कि यह या तो कुछ लोगों के लिए नहीं होता है, लेकिन उन लोगों के लिए भी, जिनके साथ ऐसा होता है, उन्हें अनिवार्य रूप से खुद को कोडबेस के भीतर काम करना चाहिए, जहां thisप्रति फ़ाइल सौ बार दिखाई देता है, और क्षति नियंत्रण का थोड़ा (या बहुत) सभी होता है एक उचित व्यक्ति उम्मीद कर सकता है। तो तीर अच्छा हो सकता है, एक तरह से, जब वे बुरी स्थिति को बेहतर बनाते हैं।

भले ही thisउनके बिना तीर के साथ कोड लिखना आसान हो , लेकिन तीर का उपयोग करने के नियम बहुत जटिल हैं (देखें: वर्तमान धागा)। इस प्रकार, दिशानिर्देश आपके अनुरोध के अनुसार न तो "स्पष्ट" हैं और न ही "सुसंगत" हैं। यहां तक ​​कि अगर प्रोग्रामर्स को तीर की अस्पष्टताओं के बारे में पता है, तो मुझे लगता है कि वे वैसे भी सिकुड़ते हैं और उन्हें स्वीकार करते हैं, क्योंकि लेक्सिकल thisओवरहैड्स का मान ।

यह सब निम्नलिखित बोध का एक प्रस्तावना है: यदि कोई उपयोग नहीं करता है this, तो thisउस तीर के बारे में अस्पष्टता सामान्य रूप से अप्रासंगिक हो जाती है। इस संदर्भ में तीर अधिक तटस्थ हो जाते हैं।

ट्रिक सिंटैक्स के बारे में

जब मैंने अपना पहला उत्तर लिखा था, तो मेरी राय थी कि सर्वोत्तम प्रथाओं का भी पालन करना एक उचित मूल्य था यदि मुझे लगता है कि मैं अधिक आदर्श कोड का उत्पादन कर सकता हूं। लेकिन मुझे अंततः एहसास हुआ कि थकाऊपन अमूर्त के रूप में काम कर सकता है जो कोड की गुणवत्ता में सुधार कर सकता है, वह भी - कभी-कभी सर्वोत्तम प्रथाओं से भटका देने के लिए पर्याप्त करने के लिए।

दूसरे शब्दों में: Dammit, मुझे वन-लाइनर फ़ंक्शन भी चाहिए!

एक दिशानिर्देश के बारे में

this-न्यूट्रल एरो फ़ंक्शंस की संभावना के साथ , और थकाऊ होने के लायक होने के बाद, मैं निम्नलिखित अधिक उदार दिशानिर्देश प्रदान करता हूं:

ES6 में फ़ंक्शन सूचना के लिए दिशानिर्देश:

  • उपयोग न करें this
  • फ़ंक्शन के लिए फ़ंक्शन घोषणाओं का उपयोग करें जिन्हें आप नाम से बुलाएंगे (क्योंकि वे फहराए गए हैं)।
  • कॉलबैक के लिए तीर फ़ंक्शंस का उपयोग करें (क्योंकि वे बहुत ख़राब होते हैं)।

नीचे "ईएस 6 में फ़ंक्शन नोटेशन के लिए दिशानिर्देश" के साथ 100% सहमत हूं - विशेष रूप से उत्थापन और इनलाइन कॉलबैक फ़ंक्शन के साथ। अच्छा उत्तर!
जेफ मैकक्लाउड

1

सरल तरीके से,

var a =20; function a(){this.a=10; console.log(a);} 
//20, since the context here is window.

एक अन्य दृष्टांत:

var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();

उत्तर: कंसोल 20 प्रिंट करेगा।

जब भी किसी फ़ंक्शन को निष्पादित किया जाता है, तो उसका स्वयं का स्टैक बनाया जाता है, इस उदाहरण में exफ़ंक्शन को newऑपरेटर के साथ निष्पादित किया जाता है, इसलिए एक संदर्भ बनाया जाएगा, और जब innerइसे निष्पादित किया जाता है , तो JS एक नया स्टैक बनाएगा और innerफ़ंक्शन को निष्पादित करेगा global contextहालांकि एक है स्थानीय संदर्भ।

इसलिए, यदि हम चाहते हैं कि innerफ़ंक्शन का एक स्थानीय संदर्भ हो, exतो हमें संदर्भ को आंतरिक फ़ंक्शन से बांधने की आवश्यकता है।

तीर इस समस्या को हल करते हैं, इसके बजाय Global contextवे लेते हैं local contextयदि कोई मौजूद है। इसमें जैसा given example,लगेगा ।new ex()this

तो, सभी मामलों में जहां बाध्यकारी स्पष्ट है तीर चूक से समस्या को हल करता है।


1

एरो फ़ंक्शंस या लैम्ब्डा, ईएस 6 में पेश किए गए थे। न्यूनतम सिंटैक्स में इसकी भव्यता के अलावा, सबसे उल्लेखनीय कार्यात्मक अंतर एक तीर फ़ंक्शन के अंदर की this ओर है।

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

में तीर कार्यों , thisहै lexically बाध्य है, जिसका अर्थ यह खत्म हो गया बंद कर देता है thisगुंजाइश है, जिसमें तीर समारोह परिभाषित किया गया था (मूल-गुंजाइश) से, और कोई बात नहीं है, जहां और यह कैसे शुरू हो जाती है / कहा जाता है नहीं बदलता है।

सीमाएं एक वस्तु पर तरीकों के रूप में तीर-कार्य

// this = global Window
let objA = {
 id: 10,
 name: "Simar",
 print () { // same as print: function() 
  console.log(`[${this.id} -> ${this.name}]`);
 }
}
objA.print(); // logs: [10 -> Simar]
objA = {
 id: 10,
 name: "Simar",
 print: () => {
  // closes over this lexically (global Window)
  console.log(`[${this.id} -> ${this.name}]`);
 }
};
objA.print(); // logs: [undefined -> undefined]

के मामले में objA.print()जब print()विधि नियमित उपयोग करके परिभाषित function , उसे ठीक करने के द्वारा काम thisकरने के लिए ठीक से objAविधि मंगलाचरण के लिए, लेकिन जब एक तीर के रूप में परिभाषित विफल रहा है =>कार्य करते हैं। यह तब होता है क्योंकि thisकिसी नियमित फ़ंक्शन में जब किसी ऑब्जेक्ट ( objA) पर एक विधि के रूप में आमंत्रित किया जाता है , तो वह ऑब्जेक्ट ही होता है। हालाँकि, एक तीर के कार्य के मामले में, thisलेक्सिक रूप से उस thisपरिक्षेत्र के दायरे में बंध जाता है , जहाँ इसे परिभाषित किया गया था (हमारे मामले में वैश्विक / विंडो) और रहने के दौरान इसकी विधि के रूप में इसके आह्वान के दौरान भी यही रहता है objA

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

/* this = global | Window (enclosing scope) */

let objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( function() {
    // invoked async, not bound to objB
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [undefined -> undefined]'
objB = {
 id: 20,
 name: "Paul",
 print () { // same as print: function() 
  setTimeout( () => {
    // closes over bind to this from objB.print()
    console.log(`[${this.id} -> ${this.name}]`);
  }, 1)
 }
};
objB.print(); // logs: [20 -> Paul]

इस मामले में, objB.print()जहां print()विधि को फ़ंक्शन के रूप में परिभाषित किया गया है जो console.log([$ {this.id} -> {this.name}] )को कॉल-बैक के रूप में अतुल्यकालिक रूप से हल करता है setTimeout , जब तीर फ़ंक्शन कॉल-बैक के रूप में उपयोग किया जाता था, लेकिन विफल रहा जब कॉल-बैक को नियमित फ़ंक्शन के रूप में परिभाषित किया गया था। इसका कारण यह है कि तीर का कार्य अपने माता-पिता से शाब्दिक रूप से बंद हो गया । जिसके आह्वान ने इसे परिभाषित किया। दूसरे शब्दों में, तीर फ़ंक्शन अपने आप में बाध्य होने के लिए पास हो गया क्योंकि आमंत्रण में ही था ।thisobjB=>setTimeout(()=>..)thisobjB.print()=>setTimeout(()==>...objBthisobjB.print() thisobjB

हम Function.prototype.bind()कॉल-बैक को एक नियमित कार्य के रूप में परिभाषित करने के लिए, इसे सही से बांधकर आसानी से उपयोग कर सकते हैं this

const objB = {
 id: 20,
 name: "Singh",
 print () { // same as print: function() 
  setTimeout( (function() {
    console.log(`[${this.id} -> ${this.name}]`);
  }).bind(this), 1)
 }
}
objB.print() // logs: [20 -> Singh]

हालाँकि, एस्कॉन कॉल-बैक के मामले में एरो फ़ंक्शंस काम में आते हैं और कम त्रुटि की संभावना होती है, जहाँ हम यह जानते thisहैं कि फ़ंक्शंस की परिभाषा उस समय में होती है, जिसमें इसे मिलता है और बाध्य होना चाहिए।

एरो-फंक्शंस की सीमा जहां इसे इनवोकेशन में बदलना होगा

कभी भी, हमें ऐसे फ़ंक्शन की आवश्यकता होती है, जिन्हें thisमंगलाचरण के समय बदला जा सकता है, हम तीर फ़ंक्शन का उपयोग नहीं कर सकते हैं।

/* this = global | Window (enclosing scope) */

function print() { 
   console.log(`[${this.id} -> {this.name}]`);
}
const obj1 = {
 id: 10,
 name: "Simar",
 print // same as print: print
};
obj.print(); // logs: [10 -> Simar]
const obj2 = {
 id: 20,
 name: "Paul",
};
printObj2 = obj2.bind(obj2);
printObj2(); // logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]

उपरोक्त में से कोई भी तीर फ़ंक्शन const print = () => { console.log([$ {this.id} -> {this.name}] के );}साथ काम thisनहीं करेगा, क्योंकि इसे बदला नहीं जा सकता है और यह thisसंलग्न क्षेत्र के लिए बाध्य रहेगा जहां इसे परिभाषित किया गया था (वैश्विक / विंडो)। इन सभी उदाहरणों में, हमने एक ही फ़ंक्शन को अलग-अलग ऑब्जेक्ट्स ( obj1और obj2) के बाद एक के बाद एक किया, दोनों को print()फ़ंक्शन घोषित होने के बाद बनाया गया था।

ये आकस्मिक उदाहरण थे, लेकिन आइए कुछ और वास्तविक जीवन उदाहरणों के बारे में सोचते हैं। अगर हमें अपनी reduce()पद्धति को एक के समान लिखना है जो काम करता है arrays , तो हम फिर से इसे लंबोदर के रूप में परिभाषित नहीं कर सकते हैं, क्योंकि इसे thisमंगलाचरण के संदर्भ से अनुमान लगाने की आवश्यकता है , अर्थात। जिस सरणी पर इसे लागू किया गया था

इस कारण से, constructorफ़ंक्शन को तीर के कार्यों के रूप में कभी भी परिभाषित नहीं किया जा सकता है, क्योंकि इसके thisनिर्माण के दौरान एक फ़ंक्शन के लिए इसकी घोषणा के समय सेट नहीं किया जा सकता है। हर बार एक कंस्ट्रक्टर फ़ंक्शन newकीवर्ड के साथ मंगाया जाता है , एक नई वस्तु बनाई जाती है जो तब उस विशेष आह्वान के लिए बाध्य हो जाती है।

जब फ्रेमवर्क या सिस्टम कॉलबैक फ़ंक्शन (ओं) को गतिशील संदर्भ के साथ बाद में स्वीकार करने के लिए स्वीकार करते हैं this , तो हम तीर फ़ंक्शन का उपयोग नहीं कर सकते हैं क्योंकि फिर thisसे हर आह्वान के साथ बदलने की आवश्यकता हो सकती है। यह स्थिति आमतौर पर DOM इवेंट हैंडलर्स के साथ होती है

'use strict'
var button = document.getElementById('button');
button.addEventListener('click', function {
  // web-api invokes with this bound to current-target in DOM
  this.classList.toggle('on');
});
var button = document.getElementById('button');
button.addEventListener('click', () => {
  // TypeError; 'use strict' -> no global this
  this.classList.toggle('on');
});

यही कारण है कि Angular 2+ और Vue.js जैसे फ्रेमवर्क में यह अपेक्षा की जाती है कि टेम्प्लेट-घटक बाइंडिंग मेथड्स नियमित फंक्शन / तरीके हों, क्योंकि thisउनके इनवोकेशन को बाइंडिंग फ़ंक्शंस के लिए फ्रेमवर्क द्वारा प्रबंधित किया जाता है। (कोणीय दृश्य-टेम्पलेट बाइंडिंग फ़ंक्शन के आक्रमण के लिए async संदर्भ का प्रबंधन करने के लिए Zone.js का उपयोग करता है)।

दूसरी ओर, रिएक्ट में , जब हम एक घटक की विधि को एक ईवेंट-हैंडलर के रूप में पास करना चाहते हैं, उदाहरण के लिए <input onChange={this.handleOnchange} />हमें handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}एक तीर फ़ंक्शन के रूप में परिभाषित करना चाहिए जैसा कि हर आह्वान के लिए होता है, हम चाहते हैं कि यह उसी घटक का उदाहरण हो जो प्रस्तुत किए गए JSX का उत्पादन करता है DOM तत्व।


यह लेख मेरे मध्यम प्रकाशन पर भी उपलब्ध है । यदि आपको आर्टिल पसंद है, या आपकी कोई टिप्पणी और सुझाव है, तो कृपया क्लैप करें या मीडियम पर टिप्पणी छोड़ दें ।

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