2012-02-28 16 views
27

वास्तविक नाम के साथ कोई फ़ंक्शन बनाने का कोई तरीका है जो eval का उपयोग किये बिना रनटाइम पर निर्धारित किया गया है, और केवल शुद्ध जावास्क्रिप्ट का उपयोग कर रहा है? (इसलिए, script तत्व उत्पन्न नहीं हुए हैं, क्योंकि वे ब्राउज़र वातावरण के लिए विशिष्ट हैं [और कई तरीकों से eval किसी भी तरह छिपाने में] होगा; कोई विशेष जावास्क्रिप्ट इंजन, आदि की गैर मानक सुविधाओं का उपयोग नहीं करते हैं)क्या रनटाइम-निर्धारित नाम के साथ कोई फ़ंक्शन बनाने का कोई गैर-eval तरीका है?

ध्यान दें कि मैं विशेष रूप से नहीं हूँ, जैसे चर या गुण होते हैं कि नाम से संदर्भित गुमनाम कार्यों के बारे में पूछ रहा:

// NOT this 
var name = /* ...come up with the name... */; 
var obj = {}; 
obj[name] = function() { /* ... */ }; 

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

उत्तर

39

ECMAScript 2015 (उर्फ "ES6") के लिए उत्तर:

हाँ। ES2015 के अनुसार, किसी ऑब्जेक्ट प्रॉपर्टी को असाइन किए गए अज्ञात फ़ंक्शन अभिव्यक्ति द्वारा बनाए गए फ़ंक्शन उस ऑब्जेक्ट प्रॉपर्टी का नाम लेते हैं। जैसा कि मैंने 11 मई 2015 को लिखा था, विंडोज 10 पूर्वावलोकन के लिए माइक्रोसॉफ्ट के "प्रोजेक्ट स्पार्टन" के अलावा जंगली में कोई जावास्क्रिप्ट इंजन इस का समर्थन करता है (हाँ, आप इसे सही पढ़ते हैं, एम $ मोज़िला या Google से पहले वहां पहुंचा) , लेकिन यह spec में है और कार्यान्वयन पकड़ जाएगा।अद्यतन: अक्टूबर 2016 तक, क्रोम के हाल के संस्करण Function#name और नाम निर्दिष्ट करने के लिए विभिन्न नियम लागू करते हैं; फ़ायरफ़ॉक्स अभी भी नहीं है, लेकिन वे वहां पहुंच जाएंगे।

तो उदाहरण के लिए, ES2015 में इस "foo ###" नामक एक समारोह बनाता है जहां ### 1-3 अंकों का होता है:

const dynamicName = "foo" + Math.floor(Math.random() * 1000); 
 
const obj = { 
 
    [dynamicName]() { 
 
    throw new Error(); 
 
    } 
 
}; 
 
const f = obj[dynamicName]; 
 
// See its `name` property (Edge and Chrome for now, eventually Firefox will get it) 
 
console.log("Function's `name` property: " + f.name + " (see compatibility note)"); 
 
// We can see that it truly has a name (even in Firefox which doesn't have the `name` property yet) via an exception: 
 
try { 
 
    f(); 
 
} catch (e) { 
 
    console.log(e.stack); 
 
}

नई ES2015 का उपयोग करता है कि का मूल्यांकन फ़ंक्शन बनाने और इसे गतिशील नाम देने के लिए प्रॉपर्टी का नाम और नया विधि सिंटैक्स, फिर f में इसका संदर्भ प्राप्त हो जाता है। (यह भी, [dynamicName]: function() { } साथ काम करेगा विधि वाक्य रचना की आवश्यकता नहीं है समारोह वाक्य रचना ठीक है।)


ECMAScript 5 के लिए उत्तर(2012 से):

नहीं, आप नहीं कर सकते हैं eval या उसके चचेरे भाई Function कन्स्ट्रक्टर के बिना ऐसा करें। आपके विकल्प हैं:

  1. इसके बजाय अज्ञात फ़ंक्शन के साथ लाइव करें। आधुनिक इंजन उन चीजों को डीबग करने में मदद करने के लिए करते हैं।

  2. eval का उपयोग करें।

  3. Function कन्स्ट्रक्टर का उपयोग करें।

विवरण:

  1. एक गुमनाम समारोह के बजाय के साथ लाइव। यदि आपके पास तकनीकी रूप से फ़ंक्शन का नाम नहीं है, तो कई आधुनिक इंजन एक उपयोगी नाम दिखाएंगे (उदा।, कॉल स्टैक और ऐसे में) यदि आपके पास एक अच्छी, स्पष्ट var name = function() { ... }; अभिव्यक्ति (चर का नाम दिखा रहा है) ईएस 6 में, इस तरह से बनाए गए कार्यों में वास्तव में नाम होंगे यदि उन्हें संदर्भ से अनुमानित किया जा सकता है। किसी भी तरह से, हालांकि, यदि आप वास्तव में रनटाइम-परिभाषित नाम (एक चर से आने वाला नाम) चाहते हैं, तो आप बहुत ज्यादा अटक गए हैं।

  2. eval का उपयोग करें। eval बुराई है जब आप इसे से बच सकते हैं, लेकिन स्ट्रिंग्स के साथ आप लागत को समझने के साथ, आप एक जावास्क्रिप्ट पार्सर को फायर कर रहे हैं), अन्यथा (इस मामले में) नहीं कर सकता है, यह ठीक है बशर्ते आपको वास्तव में उस चीज़ को करने की आवश्यकता हो। लेकिन अगर आप स्ट्रिंग या स्कोप के नियंत्रण में नहीं हैं, या आप लागत नहीं चाहते हैं, तो आपको एक अनाम फ़ंक्शन के साथ रहना होगा।

    यहाँ कैसे eval विकल्प दिखाई देता है:

    var name = /* ...come up with the name... */; 
    var f = eval(
        "(function() {\n" + 
        " function " + name + "() {\n" + 
        "  console.log('Hi');\n" + 
        " }\n" + 
        " return " + name + ";\n" + 
        "})();" 
    ); 
    

    Live example | Live source

    यह उस नाम के साथ एक फ़ंक्शन बनाता है जिसे हम नाम के दायरे में नाम लेते हुए रनटाइम पर आते हैं (और आईई 8 और पहले में नामित फ़ंक्शन एक्सप्रेशन के दोषपूर्ण हैंडलिंग को ट्रिगर किए बिना), उस फ़ंक्शन का संदर्भ निर्दिष्ट करते हुए f। (और यह कोड को अच्छी तरह से प्रारूपित करता है ताकि डीबगर में इसके माध्यम से सिंगल-स्टेपिंग आसान हो।)

    यह फ़ायरफ़ॉक्स के पुराने संस्करणों में सही नाम (आश्चर्यजनक रूप से) असाइन करने के लिए उपयोग नहीं किया गया था। फ़ायरफ़ॉक्स 2 में अपने जावास्क्रिप्ट इंजन के वर्तमान संस्करण के अनुसार, यह करता है।

    क्योंकि eval का उपयोग करता है, तो आपके द्वारा बनाए गए फ़ंक्शन में उस क्षेत्र तक पहुंच होती है जिसमें यह बनाया गया था, जो महत्वपूर्ण है यदि आप एक स्वच्छ कोडर हैं जो वैश्विक प्रतीकों से बचाता है। तो यह काम करता है, उदाहरण के लिए:

    (function() { 
        function display(msg) { 
         var p = document.createElement('p'); 
         p.innerHTML = String(msg); 
         document.body.appendChild(p); 
        } 
    
        var name = /* ...come up with the name... */; 
        var f = eval(
         "(function() {\n" + 
         " function " + name + "() {\n" + 
         "  display('Hi');\n" +   // <=== Change here to use the 
         " }\n" +       //  function above 
         " return " + name + ";\n" + 
         "})();" 
        ); 
    })(); 
    
  3. उपयोग Function निर्माता, this article by Marcos Cáceres में प्रदर्शन के रूप में:

    var f = new Function(
        "return function " + name + "() {\n" + 
        " display('Hi!');\n" + 
        " debugger;\n" + 
        "};" 
    )(); 
    

    Live example | Live source

    वहां हम एक अस्थायी अज्ञात फ़ंक्शन बनाते हैं (जिसे Function कन्स्ट्रक्टर के माध्यम से बनाया गया है) और इसे कॉल करें; वह अस्थायी अनाम फ़ंक्शन एक नामित फ़ंक्शन अभिव्यक्ति का उपयोग करके एक नामित फ़ंक्शन बनाता है। वह IE8 और इससे पहले नामित फ़ंक्शन एक्सप्रेशन के दोषपूर्ण हैंडल को ट्रिगर करेगा, लेकिन इससे कोई फर्क नहीं पड़ता, क्योंकि इसके दुष्प्रभाव अस्थायी फ़ंक्शन तक ही सीमित हैं।

    यह eval संस्करण की तुलना में कम है, लेकिन एक मुद्दा है: Function निर्माता के माध्यम से बनाया कार्य नहीं गुंजाइश है, जिसमें वे बनाए गए थे करने के लिए पहुँच है।तो ऊपर दिए गए उदाहरण display का उपयोग विफल हो जाएंगे, क्योंकि display बनाए गए फ़ंक्शन के लिए इन-स्कोप नहीं होगा। (Here's an example इसमें विफल रहा है। Source)। तो वैश्विक प्रतीकों से परहेज करने वाले स्वच्छ कोडर के लिए एक विकल्प नहीं है, लेकिन उन समय के लिए उपयोगी है जब आप चाहते हैं जो जेनरेट किए गए फ़ंक्शन को उस दायरे से अलग करने के लिए चाहते हैं जिसमें आप इसे उत्पन्न कर रहे हैं।

+0

अज्ञात फ़ंक्शन के बारे में इतना बुरा क्या है? – PiTheNumber

+0

@PiTheNumber: मैंने यह नहीं कहा कि वे बुरे थे। :-) वास्तव में मैंने कहा * "बेनामी फ़ंक्शंस बहुत सी चीजों के लिए ठीक है ..." * लेकिन अज्ञात फ़ंक्शन कॉल स्टैक में दिखाई देते हैं और जैसे "(अनाम)" (या समान), जो उपयोगी से कम है। मुझे पसंद है [मेरे उपकरण मेरी मदद करें] (http://blog.niftysnippets.org/2010/03/anonymouses-anonymous.html), इसलिए जब भी मैं कर सकता हूं तो मैं अपने कार्यों के नाम देता हूं। इस मामले में, मैं एक लाइब्रेरी में फ़ंक्शन फ़ैक्टरी फ़ंक्शन लिख रहा हूं, और यह तय कर रहा हूं कि यह (वैकल्पिक रूप से) कार्यों को वास्तविक नाम, या इस मामले में कोई नाम या स्थिर नाम नहीं दे रहा है या नहीं। –

+2

एमएच, मैं आपको इंगित करता हूं। खैर, मैं अज्ञात कार्यों से बचने के बारे में सोचूंगा। धन्यवाद! – PiTheNumber

6

यहां कुछ उपयोगिता कार्य है जो मैं कुछ समय पहले आया था। यह Function कन्स्ट्रक्टर तकनीक का उपयोग करता है जैसा कि @ टीजेड्रोडर के महान उत्तर में उल्लिखित है, लेकिन इसके नुकसान में सुधार करता है और नए कार्य के दायरे पर जुर्माना नियंत्रण की अनुमति देता है।

function NamedFunction(name, args, body, scope, values) { 
    if (typeof args == "string") 
     values = scope, scope = body, body = args, args = []; 
    if (!Array.isArray(scope) || !Array.isArray(values)) { 
     if (typeof scope == "object") { 
      var keys = Object.keys(scope); 
      values = keys.map(function(p) { return scope[p]; }); 
      scope = keys; 
     } else { 
      values = []; 
      scope = []; 
     } 
    } 
    return Function(scope, "function "+name+"("+args.join(", ")+") {\n"+body+"\n}\nreturn "+name+";").apply(null, values); 
}; 

यह आप जा रहा है साफ औरeval के माध्यम से अपने दायरे में पूर्ण पहुंच से परहेज करें, जैसे की अनुमति देता है उपर्युक्त परिदृश्य में:

var f = NamedFunction("fancyname", ["hi"], "display(hi);", {display:display}); 
f.toString(); // "function fancyname(hi) { 
       // display(hi); 
       // }" 
f("Hi"); 
संबंधित मुद्दे

 संबंधित मुद्दे