तर्क क्यों थे।


214

arguments.callee.callerसंपत्ति को जावास्क्रिप्ट में अपदस्थ क्यों किया गया था ?

यह जोड़ा गया था और फिर जावास्क्रिप्ट में पदावनत किया गया था, लेकिन यह पूरी तरह से ECMAScript द्वारा छोड़ा गया था। कुछ ब्राउज़र (मोज़िला, IE) ने हमेशा इसका समर्थन किया है और समर्थन हटाने के लिए नक्शे पर कोई योजना नहीं है। दूसरों (सफारी, ओपेरा) ने इसके लिए समर्थन को अपनाया है, लेकिन पुराने ब्राउज़रों पर समर्थन अविश्वसनीय है।

क्या इस मूल्यवान कार्यक्षमता को सीमित करने का एक अच्छा कारण है?

(या वैकल्पिक रूप से, कॉलिंग फ़ंक्शन पर हैंडल को हथियाने का एक बेहतर तरीका है?)


2
यह अन्य ब्राउज़रों द्वारा समर्थित है क्योंकि किसी भी सुविधा को व्यापक उपयोग का एक मोडिक मिल जाता है जो अन्य ब्राउज़र के लिए संगतता बग बन जाएगा। यदि कोई साइट केवल एक ब्राउज़र में मौजूद किसी सुविधा का उपयोग करती है, तो साइट अन्य सभी में टूट जाती है, और आमतौर पर उपयोगकर्ता सोचते हैं कि यह ब्राउज़र टूट गया है।
olliej

4
(लगभग सभी ब्राउज़रों ने एक समय या किसी अन्य पर किया है, उदाहरण के लिए। यह सुविधा (और JS स्वयं) नेटस्केप, XHR से आईई, कैनवस इन सफारी, आदि में उत्पन्न होती है, इनमें से कुछ उपयोगी हैं और अन्य ब्राउज़र द्वारा उठाए गए हैं समय के साथ (js, कैनवस, xhr सभी उदाहरण हैं), कुछ (.callee) नहीं हैं।
olliej

@olliej इसका समर्थन करने के बारे में आपकी टिप्पणी क्योंकि इसका उपयोग किया जाता है और इसलिए नहीं कि यह एक मानक है (या मानक में पदावनत होने के बावजूद भी) बहुत सही है! यही कारण है कि मैंने ज्यादातर मानकों को अनदेखा करना शुरू कर दिया जब भी मुझे लगता है कि वे मेरी मदद नहीं कर रहे हैं। हम डेवलपर्स के रूप में मानकों का निर्देशन कर सकते हैं कि क्या काम करता है और क्या युक्ति नहीं कहती है कि हमें क्या करना चाहिए। यह है कि हम कैसे <b>और <i>वापस आ गए (हाँ, उन लोगों को एक बिंदु पर हटा दिया गया था)।
स्टिजन डे विट अप

जवाबों:


252

जावास्क्रिप्ट के शुरुआती संस्करणों ने नाम की अभिव्यक्ति को अनुमति नहीं दी, और इस वजह से हम एक पुनरावर्ती फ़ंक्शन अभिव्यक्ति नहीं बना सके:

 // This snippet will work:
 function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 }
 [1,2,3,4,5].map(factorial);


 // But this snippet will not:
 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
 });

इसके आसपास जाने के लिए, arguments.calleeहमें इसमें जोड़ा गया था:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

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

var global = this;
var sillyFunction = function (recursed) {
    if (!recursed)
        return arguments.callee(true);
    if (this !== global)
        alert("This is: " + this);
    else
        alert("This is the global");
}
sillyFunction();

किसी भी तरह, EcmaScript 3 ने इन समस्याओं को हल करने के लिए फ़ंक्शन फ़ंक्शन नाम की अनुमति दी है, जैसे:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

इसके कई लाभ हैं:

  • फ़ंक्शन को आपके कोड के अंदर से किसी भी अन्य की तरह कहा जा सकता है।

  • यह नाम स्थान को प्रदूषित नहीं करता है।

  • मूल्य thisनहीं बदलता है।

  • यह अधिक प्रदर्शन करने वाला है ( तर्कों को एक्सेस करना महंगा है)।

ओह,

बस एहसास है कि सब कुछ के अलावा सवाल के बारे में था arguments.callee.caller, या अधिक विशेष रूप से Function.caller

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

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

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

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

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


12
क्या आप कह रहे हैं कि यह सिर्फ इसलिए वंचित है क्योंकि इसे अनुकूलित करना कठिन है? यह थोड़े मूर्खतापूर्ण है।
थॉमस ईडिंग

11
नहीं, मैंने कई कारणों को सूचीबद्ध किया है, इसके अलावा इसे अनुकूलित करना कठिन बना है (हालांकि सामान्य इतिहास में यह दिखाया गया है कि जिन चीज़ों को अनुकूलित करना कठिन है उनमें भी शब्दार्थ है कि लोगों को कठिनाई का सामना करना पड़ता है)
olliej

17
इस तर्क थोड़ा नकली अपने मूल्य अगर यह महत्वपूर्ण है कॉल से सेट किया जा सकता है। आमतौर पर इसका इस्तेमाल नहीं किया जाता है (कम से कम, मैंने कभी इसके साथ पुनरावर्ती कार्यों में कोई समस्या नहीं की है)। समारोह को नाम से पुकारना एक ही मुद्दा है thisइसलिए मुझे लगता है कि यह इस संबंध में अप्रासंगिक है कि कैली अच्छी है या बुरी। इसके अलावा, कैली और कॉलर केवल सख्त मोड में "पदावनत" हैं (ECMAscript ed 5, Dec 2009), लेकिन मुझे लगता है कि 2008 में जब olliej पोस्ट नहीं किया गया था तो मुझे पता नहीं था।
रॉबजी

8
) मैं अभी भी तर्क नहीं देखता हूं। प्रथम श्रेणी के कार्यों के साथ किसी भी भाषा में, एक फ़ंक्शन बॉडी को परिभाषित करने में सक्षम होने के लिए स्पष्ट मूल्य है जो मुझे पता
मार्क रीड रीड

8
RobG इस ने कहा, लेकिन मुझे नहीं लगता कि यह था सब स्पष्ट है कि: एक नामित फ़ंक्शन का उपयोग केवल का मूल्य सुरक्षित करेगा recursing thisअगर thisवैश्विक गुंजाइश है। अन्य सभी मामलों में, पहली पुनरावर्ती कॉल के बाद मूल्य बदल this जाएगा , इसलिए मुझे लगता है कि आपके उत्तर के हिस्से संरक्षण के thisलिए वास्तव में मान्य नहीं हैं।
JLRishe

89

arguments.callee.callerहै पदावनत है, हालांकि इसके बारे में मेकअप उपयोग करता है संपत्ति। ( बस आपको वर्तमान फ़ंक्शन का संदर्भ देगा)Function.callerarguments.callee

  • Function.callerहालांकि ECMA3 के अनुसार गैर-मानक, सभी वर्तमान प्रमुख ब्राउज़रों में लागू किया गया है
  • arguments.caller के पक्ष में पदावनत किया जाता है , और कुछ वर्तमान प्रमुख ब्राउज़रों (जैसे फ़ायरफ़ॉक्स 3) में लागू नहीं किया जाता है।Function.caller

इसलिए स्थिति आदर्श से कम है, लेकिन यदि आप सभी प्रमुख ब्राउज़रों में जावास्क्रिप्ट में कॉलिंग फ़ंक्शन को एक्सेस करना चाहते हैं, तो आप प्रॉपर्टी का उपयोग कर सकते हैं, या तो सीधे नाम फ़ंक्शन संदर्भ पर या संपत्ति के माध्यम से एक अनाम फ़ंक्शन के भीतर से एक्सेस किया जा सकता है।Function.callerarguments.callee


5
यह इस बात का सबसे अच्छा विवरण है कि बहुत उपयोगी नहीं है। Function.caller क्या कर सकता है (पुनरावर्ती कार्यों के ढेर का पता लगाएं) के एक अच्छे उदाहरण के लिए, डेवलपर.
mozilla.org/en/JavaScript/Reference/Global_Objects/…

1
हालांकि, arguments.calleeसख्त मोड में निषिद्ध है। इसने मुझे बहुत दुखी किया, लेकिन अब इसका इस्तेमाल नहीं करना बेहतर है।
ग्रास डबल

1
Arguments.callee हाइपरलिंक आप MDN करने के लिए है का कहना है कि यह कठोर मोड में निकाल दिया जाता है। क्या यह वैसा ही नहीं है जैसा कि वंचित है?
स्टाइलफेल

1
ध्यान दें कि arguments.callee.callerES5 सख्त मोड में पदावनत किया गया है: "एक और विशेषता जो पदावनत की गई थी, वह थी तर्क। callee.caller, या अधिक विशेष रूप से Function.caller।" ( स्रोत )
थूडान

29

तर्कों की तुलना में नामित कार्यों का उपयोग करना बेहतर है।

 function foo () {
     ... foo() ...
 }

से बेहतर है

 function () {
     ... arguments.callee() ...
 }

नामित फ़ंक्शन को कॉलर प्रॉपर्टी के माध्यम से इसके कॉलर तक पहुंच प्राप्त होगी :

 function foo () {
     alert(foo.caller);
 }

जो से बेहतर है

 function foo () {
     alert(arguments.callee.caller);
 }

पदावनत वर्तमान ECMAScript डिजाइन सिद्धांतों के कारण है ।


2
क्या आप बता सकते हैं कि नामित फ़ंक्शन का उपयोग करना बेहतर क्यों है। क्या किसी अनाम फ़ंक्शन में कैली का उपयोग करने की आवश्यकता नहीं है?
एंथनीवजोन

27
यदि आप एक गुमनाम फ़ंक्शन में कैली का उपयोग कर रहे हैं, तो आपके पास एक फ़ंक्शन है जो गुमनाम नहीं होना चाहिए।
प्रेस्टुल

3
कभी-कभी डीबग करने का सबसे आसान तरीका है .caller ()। ऐसे मामलों में फ़ंक्शंस मदद नहीं करेंगे - आप यह जानने की कोशिश कर रहे हैं कि कौन सा फ़ंक्शन कॉलिंग कर रहा है।
सैमगुडी

6
बेहतर परिभाषित करें। उदाहरण के लिए, IE6-8 ने तर्कों का नाम दिया है जबकि तर्कों में। कार्य करता है।
सेमी

1
IE6-8 quirks के अलावा, यह कोड को कसकर युग्मित भी बनाता है। यदि ऑब्जेक्ट्स और / या फ़ंक्शंस के नाम हार्डकोड किए गए हैं, तो जैसा कि ardsasd और rsk82 उल्लेख करते हैं कि बड़े रीफैक्टरिंग खतरे हैं, जो केवल कोड बेस साइज़ बढ़ने पर बढ़ते हैं। यूनिट परीक्षण रक्षा की एक पंक्ति है, और मैं उनका उपयोग करता हूं, लेकिन वे अभी भी एक जवाब नहीं हैं जो वास्तव में मुझे इस हार्डकॉकिंग मुद्दे के बारे में व्यक्तिगत रूप से तृप्त करता है।
जैस्मीन हेगमैन

0

बस एक विस्तार। पुनरावृत्ति के दौरान "यह" का मूल्य बदल जाता है। निम्नलिखित (संशोधित) उदाहरण में, भाज्य को {foo: true} ऑब्जेक्ट मिलता है।

[1,2,3,4,5].map(function factorial(n) {
  console.log(this);
  return (!(n>1))? 1 : factorial(n-1)*n;
},     {foo:true}     );

पहली बार कहा जाने वाला गुट वस्तु हो जाता है, लेकिन पुनरावर्ती कॉल के लिए यह सच नहीं है।


1
क्योंकि आप गलत कर रहे हैं। यदि thisबनाए रखने की आवश्यकता है, तो लिखें कि factorial.call(this, n-1)मैंने वास्तव में पुनरावर्ती कोड लिखने में पाया है, कि आमतौर पर कोई पेड़ नहीं है this, या thisएक पेड़ में कुछ नोड को संदर्भित करता है और वास्तव में यह अच्छा है कि यह बदल जाता है।
स्टिजन डे विट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.