递归挂钩时,Java脚本会丢失上下文

| 我开始使用JS动态分析工具,并且希望对整个环境进行简要介绍。我基本上遍历各种上下文,深入研究对象,每次我点击一个函数时,我都会迷上它。现在,除了在处理诸如jQuery / prototype之类的库时会中断的事实之外,这相对来说效果很好。 到目前为止,这是我的代码(尽我所能):
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\");
只要函数简单且非匿名,遍历就可以正常工作,钩子也可以正常工作(我认为钩住匿名函数是不可能的,因此我对此不太担心)。 这是预处理阶段中一些经过调整的输出。
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
问题是我认为我在打电话时失去了
this
上下文
return fn.apply(parent, arguments);
这是另一个有趣的怪癖。如果我在遍历之前就上钩了,即:
        // 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
,但最终我想做堆栈跟踪和时间函数(通过Java applet)。 这个问题最终变得很严重,对不起,我们将不胜感激! 注意:如果您不小心,以上代码可能会导致浏览器崩溃。一般警告:P     
已邀请:
        我认为您的方向正确。当您使用
apply
时,ѭ6的值会崩溃。 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\");
在此,该功能取决于
this
的值来打印文本
Monkeys are funny!
。如您所见,使用
hook
算法,该上下文丢失了。 Firebug显示: 猴子很有趣! 钩到obj.fn 没有猴子:( 我做了一个小小的改动,在申请中使用了
this
,而不是
obj
(父母):
obj.fn = function() {
   console.log(\"hooking to obj.fn\");
   return ref.apply(this);
};
这次Firebug说: 猴子很有趣! 钩到obj.fn 猴子很有趣! 问题恕我直言的根源是您正在为
this
设置一个显式值(即,
parent
指向父对象)。因此,钩子函数最终将覆盖
this
的值,该值可能已由调用原始函数的任何代码显式设置。当然,该代码不知道您用自己的钩子函数包装了原始函数。因此,钩子函数在调用原始函数时应保留
this
的值:
return fn.apply(this, arguments);
因此,如果您在申请中使用
this
而不是
parent
,则可能会解决问题。 如果无法正确理解您的问题,我们深表歉意。无论我错在哪里,都请纠正我。 更新 jQuery中有两种功能。附加到
jQuery
对象本身的对象(类似于静态方法),然后您可以对
jQuery(selector)
的结果进行操作(类似于实例方法)。后者是您需要关注的。在这里,ѭ6非常重要,因为这就是实现链接的方式。 我能够使以下示例正常工作。请注意,我正在处理对象的实例,而不是对象本身。因此,在您的示例中,我将研究
jQuery(\"#someId\")
而不仅仅是just35ѭ:
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源将为您提供更多的见解:)。我认为对您来说困难在于将通过ѭ15调用的函数与通过链接调用(或直接调用)的函数区分开。     

要回复问题请先登录注册