2011-05-27 15 views
6

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

यह मेरा कोड अब तक (मेरी क्षमता के अनुसार टिप्पणी की) है:

var __PROFILER_global_props = new Array(); // visited properties 

/** 
* Hook into a function 
* @name the name of the function 
* @fn the reference to the function 
* @parent the parent object 
*/ 
function __PROFILER_hook(name, fn, parent) { 
    //console.log('hooking ' + name + ' ' + fn + ' ' + parent); 

    if (typeof parent == 'undefined') 
     parent = window; 

    for (var i in parent) { 
     // find the right function 
     if (parent[i] === fn) { 
      // hook into it 
      console.log('--> hooking ' + name); 
       parent[i] = function() { 
         console.log('called ' + name); 
         return fn.apply(parent, arguments); 
       } 

       //parent[i] = fn; // <-- this works (obviously) 
       break; 
     } 
    } 
} 

/** 
* Traverse object recursively, looking for functions or objects 
* @obj the object we're going into 
* @parent the parent (used for keeping a breadcrumb) 
*/ 
function __PROFILER_traverse(obj, parent) { 
    for (i in obj) { 
     // get the toString object type 
     var oo = Object.prototype.toString.call(obj[i]); 
     // if we're NOT an object Object or an object Function 
     if (oo != '[object Object]' && oo != '[object Function]') { 
      console.log("...skipping " + i); 
      // skip 
      // ... the reason we do this is because Functions can have sub-functions and sub-objects (just like Objects) 
      continue; 
     } 
     if (__PROFILER_global_props.indexOf(i) == -1 // first we want to make sure we haven't already visited this property 
      && (i != '__PROFILER_global_props'  // we want to make sure we're not descending infinitely 
      && i != '__PROFILER_traverse'   // or recusrively hooking into our own hooking functions 
      && i != '__PROFILER_hook'    // ... 
      && i != 'Event'    // Event tends to be called a lot, so just skip it 
      && i != 'log'    // using FireBug for debugging, so again, avoid hooking into the logging functions 
      && i != 'notifyFirebug')) {   // another firebug quirk, skip this as well 

      // log the element we're looking at 
      console.log(parent+'.'+i); 
      // push it.. it's going to end up looking like '__PROFILER_BASE_.something.somethingElse.foo' 
      __PROFILER_global_props.push(parent+'.'+i); 
      try { 
       // traverse the property recursively 
       __PROFILER_traverse(obj[i], parent+'.'+i); 
       // hook into it (this function does nothing if obj[i] is not a function) 
       __PROFILER_hook(i, obj[i], obj); 
      } catch (err) { 
       // most likely a security exception. we don't care about this. 
      } 
     } else { 
      // some debugging 
      console.log(i + ' already visited'); 
     } 
    } 
} 

यही कारण है कि प्रोफाइल है और यह कैसे मैं यह आह्वान है:

// traverse the window 
__PROFILER_traverse(window, '__PROFILER_BASE_'); 

// testing this on jQuery.com 
$("p.neat").addClass("ohmy").show("slow"); 

traversing ठीक काम करता है और hooking ठीक काम करता है जब तक कि कार्यों को सरल और गैर-अनाम (मैं गुमनाम कार्यों में hooking लगता है कर रहे हैं असंभव है तो मैं नहीं कर रहा हूँ इसके बारे में बहुत चिंतित)।

यह प्रीप्रोकैसिंग चरण से कुछ छंटनी आउटपुट है।

notifyFirebug already visited 
...skipping firebug 
...skipping userObjects 
__PROFILER_BASE_.loadFirebugConsole 
--> hooking loadFirebugConsole 
...skipping location 
__PROFILER_BASE_.$ 
__PROFILER_BASE_.$.fn 
__PROFILER_BASE_.$.fn.init 
--> hooking init 
...skipping selector 
...skipping jquery 
...skipping length 
__PROFILER_BASE_.$.fn.size 
--> hooking size 
__PROFILER_BASE_.$.fn.toArray 
--> hooking toArray 
__PROFILER_BASE_.$.fn.get 
--> hooking get 
__PROFILER_BASE_.$.fn.pushStack 
--> hooking pushStack 
__PROFILER_BASE_.$.fn.each 
--> hooking each 
__PROFILER_BASE_.$.fn.ready 
--> hooking ready 
__PROFILER_BASE_.$.fn.eq 
--> hooking eq 
__PROFILER_BASE_.$.fn.first 
--> hooking first 
__PROFILER_BASE_.$.fn.last 
--> hooking last 
__PROFILER_BASE_.$.fn.slice 
--> hooking slice 
__PROFILER_BASE_.$.fn.map 
--> hooking map 
__PROFILER_BASE_.$.fn.end 
--> hooking end 
__PROFILER_BASE_.$.fn.push 
--> hooking push 
__PROFILER_BASE_.$.fn.sort 
--> hooking sort 
__PROFILER_BASE_.$.fn.splice 
--> hooking splice 
__PROFILER_BASE_.$.fn.extend 
--> hooking extend 
__PROFILER_BASE_.$.fn.data 
--> hooking data 
__PROFILER_BASE_.$.fn.removeData 
--> hooking removeData 
__PROFILER_BASE_.$.fn.queue 

जब मैं jQuery.com (Firebug के माध्यम से) पर $("p.neat").addClass("ohmy").show("slow"); निष्पादित, मैं एक उचित कॉल स्टैक मिलता है, लेकिन मैंने कहीं रास्ते में मेरी संदर्भ खोने के लिए कुछ नहीं होता क्योंकि लगते हैं और मैं (jQuery से एक e is undefined त्रुटि मिलती है स्पष्ट रूप से, हुकिंग कुछ खराब कर दिया)।

called init 
called init 
called find 
called find 
called pushStack 
called pushStack 
called init 
called init 
called isArray 
called isArray 
called merge 
called merge 
called addClass 
called addClass 
called isFunction 
called isFunction 
called show 
called show 
called each 
called each 
called isFunction 
called isFunction 
called animate 
called animate 
called speed 
called speed 
called isFunction 
called isFunction 
called isEmptyObject 
called isEmptyObject 
called queue 
called queue 
called each 
called each 
called each 
called each 
called isFunction 
called isFunction 

समस्या मुझे लगता है कि जब

return fn.apply(parent, arguments); 

यहाँ एक और दिलचस्प मोड़ है बुला रहा this संदर्भ खो रहा हूँ है। अगर मैं इससे पहले कि मैं पार, यानी हुक:

 // hook into it (this function does nothing if obj[i] is not a function) 
     __PROFILER_hook(i, obj[i], obj); 
     // traverse the property recursively 
     __PROFILER_traverse(obj[i], parent+'.'+i); 

.. आवेदन बिलकुल ठीक चलाता है, लेकिन कॉल स्टैक बदल दिया जाता है (और मैं jQuery-विशिष्ट कार्यों पाने के लिए नहीं लग रहे) किसी कारण से:

called $ 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called setInterval 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called getComputedStyle 
called clearInterval 

.. बजाय animation, show, merge, आदि अभी, सब हुक करता called functionName कहना लेकिन अंत में मैं (एक जावा एप्लेट के माध्यम से) स्टैक ट्रेस और समय कार्यों क्या करना चाहते हैं।

यह प्रश्न बड़ा हो गया और मैं क्षमा चाहता हूं, लेकिन किसी भी मदद की सराहना की जाती है!

नोट: यदि आप सावधान नहीं हैं तो उपरोक्त कोड आपके ब्राउज़र को क्रैश कर सकता है। उचित चेतावनी: पी

+0

इसी प्रकार का प्रश्न: http://stackoverflow.com/questions/5033836/adding-console-log-to-every-function- स्वचालित रूप से –

उत्तर

3

मुझे लगता है कि आप सही रास्ते पर हैं। जब आप apply का उपयोग कर रहे हों तो this का मान गिर रहा है। JQuery के भीतर परिभाषित कार्यों को शायद आंतरिक रूप से apply के माध्यम से बुलाया जा रहा है, और this के मान पर निर्भर करता है।

apply पर पहला तर्क वह मान है जिसका उपयोग this के लिए किया जाएगा। क्या आप वाकई parent का उपयोग कर रहे हैं?

मैं निम्नलिखित तरीके से समस्या नकल करने में सक्षम था:

var obj = { 
    fn : function() { 
     if(this == "monkeys") { 
     console.log("Monkeys are funny!"); 
     } 

     else { 
     console.log("There are no monkeys :("); 
     } 
    } 
}; 

obj.fn.apply("monkeys"); 

var ref = obj.fn; 

//assuming parent here is obj 
obj.fn = function() { 
    console.log("hooking to obj.fn"); 
    return ref.apply(obj); 
}; 

obj.fn.apply("monkeys"); 

इधर, समारोह पाठ Monkeys are funny! मुद्रित करने के लिए this के मूल्य पर निर्भर करता है। जैसा कि आप देख सकते हैं, अपने hook एल्गोरिदम का उपयोग करके, यह संदर्भ खो गया है। Firebug पता चलता है:

Monkeys are funny! 
hooking to obj.fn 
There are no monkeys :(

मैं एक मामूली परिवर्तन किया और में this इस्तेमाल किया obj (पैरेंट) के बजाय लागू होते हैं:

obj.fn = function() { 
    console.log("hooking to obj.fn"); 
    return ref.apply(this); 
}; 

इस बार Firebug का कहना है:

Monkeys are funny! 
hooking to obj.fn 
Monkeys are funny!

जड़ आपकी समस्या का IMHO यह है कि आप this (यानी parent) के लिए एक स्पष्ट मान निर्धारित कर रहे हैं जो मूल वस्तु को संदर्भित करता है)। तो आपका हुक फ़ंक्शन this के मान को ओवरराइट करने के समाप्त होता है, जो कि मूल फ़ंक्शन को कॉल करने वाले किसी भी कोड द्वारा स्पष्ट रूप से सेट किया गया हो सकता है। बेशक, वह कोड नहीं जानता है कि आपने मूल फ़ंक्शन को अपने स्वयं के हुक फ़ंक्शन से लपेट लिया है। तो

return fn.apply(this, arguments); 

अगर आप अपनी लागू होते हैं, समस्या समाधान किया जा सकता है this बजाय parent का उपयोग करें: तो अपने हुक समारोह जब यह मूल कार्य बुला रहा है this का मूल्य की रक्षा करना चाहिए।

अगर मैं आपकी समस्या को सही ढंग से समझ नहीं पा रहा हूं तो मैं क्षमा चाहता हूं। जहां भी मैं गलत हूं कृपया मुझे सही करें।

अद्यतन

jQuery में कार्यों के दो प्रकार होते हैं। वे लोग जो jQuery ऑब्जेक्ट से जुड़े होते हैं (स्थिर विधियों की तरह) और फिर आपके पास jQuery(selector) (उदाहरण उदाहरणों की तरह) के परिणामस्वरूप संचालित होते हैं। यह बाद वाला है जिसके बारे में आपको चिंतित होना चाहिए। यहां, this बहुत मायने रखता है क्योंकि इस तरह आप चेनिंग को कार्यान्वित करते हैं।

मैं काम करने के लिए निम्नलिखित उदाहरण प्राप्त करने में सक्षम था। ध्यान दें कि मैं ऑब्जेक्ट के इंस्टेंस पर ऑब्जेक्ट पर काम कर रहा हूं और ऑब्जेक्ट नहीं। अपने उदाहरण में तो, मैं jQuery("#someId") पर काम किया जाएगा और नहीं पर सिर्फ jQuery:

var obj = function(element) { 
    this.element = element; 
    this.fn0 = function(arg) { 
     console.log(arg, element); 
     return this; 
    } 

    this.fn1 = function(arg) { 
     console.log(arg, arg, element); 
     return this; 
    } 

    if(this instanceof obj) { 
     return this.obj; 
    } 

    else { 
     return new obj(element); 
    } 
}; 

var objInst = obj("monkeys"); 

var ref0 = objInst.fn0; 

objInst.fn0 = function(arg) { 
    console.log("calling f0"); 
    return ref0.apply(this, [arg]); 
}; 

var ref1 = objInst.fn1; 

objInst.fn1 = function(arg) { 
    console.log("calling f1"); 
    return ref1.apply(this, [arg]); 
}; 

objInst.fn0("hello").fn1("bye"); 

मैं इस आपकी समस्या नहीं सुलझती है या नहीं करता है, तो पता नहीं है। शायद jQuery स्रोत में देखकर आपको कुछ और अंतर्दृष्टि मिल जाएगी :)। I सोचें कि आपके लिए कठिनाई उन कार्यों के बीच अंतर करना है जिन्हें apply और चेनिंग (या केवल सीधे कहा जाता है) के माध्यम से बुलाया जाता है।

+0

आप इसे पूरी तरह से समझ चुके हैं, और मैंने पहले 'यह' कोशिश की थी, लेकिन अगर मैं उपयोग करता हूं 'यह', ऐसा प्रतीत होता है कि फ़ंक्शन चेनिंग ब्रेक (इसलिए '$ (' कुछ ') जैसे कुछ। addClass (' asdfg ')। दिखाएं (' धीमी ')' काम करना बंद कर देता है)। विशेष रूप से, मुझे लगता है कि 'पुश() 'अपरिभाषित है। –

+0

@ डेविड मैंने अपना जवाब अपडेट किया।मुझे नहीं पता कि यह आपके प्रश्न का उत्तर देता है या नहीं, लेकिन मैंने कोशिश की: पी मैंने जवाब में इसका उल्लेख किया है, लेकिन मुझे लगता है कि जब आप 'लागू' के माध्यम से बुलाए गए कार्यों के बीच अंतर करने की कोशिश करते हैं तो आपको कठिनाई का सामना करना पड़ेगा 'वे बनाम जिन्हें चेनिंग या सीधे कहा जाता है। मुझे इसके समाधान को जानने में भी दिलचस्पी है, इसलिए उम्मीद है कि कोई और जो मुझे अधिक से अधिक जानकार है, मैं इसमें शामिल हो सकता हूं। –

+0

हालांकि यह सामान्य मामले में समस्या का समाधान नहीं कर सकता है, लेकिन मेरा अंतर्ज्ञान मुझे बताता है कि यह संभव होना चाहिए और मैं आपकी अंतर्दृष्टि की सराहना करता हूं। मुझे लगता है कि जब आप विधि श्रृंखला और स्थैतिक विधियों के बीच भेद बनाते हैं तो आप सिर पर नाखून मारा। साथ ही, कुछ परीक्षणों को मैंने पाया कि कभी-कभी कॉलिंग अपने स्वयं के सरणी में लपेटें तर्क लागू करती है .. जो विषम है। –

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