首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >遍历arguments.callee.caller会导致无限循环

遍历arguments.callee.caller会导致无限循环
EN

Stack Overflow用户
提问于 2013-03-23 09:22:49
回答 3查看 913关注 0票数 0

假设我想要获取某种堆栈跟踪,获取在当前函数之前调用的所有函数的名称。

我这样做:

代码语言:javascript
运行
复制
        var callee;
        var caller;
        var _args = arguments;
        var check = 0;
        do {
                    check++;

                    callee = _args.callee;
                    caller = callee.caller;

                    var msg = 'Check ' + check + ' - ' + callee.name 
                            + ' has been called by: ' + caller.name;
                    console.log(msg);

                    if (caller) {
                        // Get this caller's arguments
                        _args = caller.arguments;
                    } else {
                        reached_end = true;
                    }

        } while (!reached_end);

这在大多数情况下都运行得很好。但有时它会陷入无限循环,我想知道:这是怎么可能的?我能做些什么呢?

下面是我的无限循环的输出:

代码语言:javascript
运行
复制
    Check 1 - __parent__ has been called by: add
    Check 2 - add has been called by: afterComponentStartup
    Check 3 - afterComponentStartup has been called by: _launchComponents [arg0:"startup"]
    Check 4 - _launchComponents has been called by: beforeActionNext
    Check 5 - beforeActionNext has been called by: beforeAction
    Check 6 - beforeAction has been called by: afterComponentInitialize
    Check 7 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"]
    Check 8 - _launchComponents has been called by: beforeActionNext
    Check 9 - beforeActionNext has been called by: beforeAction
    Check 10 - beforeAction has been called by: afterComponentInitialize
    Check 11 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"]
    Check 12 - _launchComponents has been called by: beforeActionNext
    Check 13 - beforeActionNext has been called by: beforeAction
    Check 14 - beforeAction has been called by: afterComponentInitialize
    Check 15 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"]
    Check 16 - _launchComponents has been called by: beforeActionNext
    Check 17 - beforeActionNext has been called by: beforeAction
    Check 18 - beforeAction has been called by: afterComponentInitialize
    Check 19 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"]
    Check 20 - _launchComponents has been called by: beforeActionNext
    Check 21 - beforeActionNext has been called by: beforeAction
    Check 22 - beforeAction has been called by: afterComponentInitialize
    Check 23 - afterComponentInitialize has been called by: _launchComponents [arg0:"startup"]
    Check 24 - _launchComponents has been called by: beforeActionNext
    Check 25 - beforeActionNext has been called by: beforeAction
    Check 26 - beforeAction has been called by: afterComponentInitialize
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-23 10:02:29

arguments.callee.caller指向函数引用,它只存在于每个调用堆栈中的每个函数中的一个。

每次调用函数时,都会设置caller属性,这意味着如果同一函数在调用堆栈中被多次调用(递归函数会发生这种情况),则先前的值将被重置,并且caller现在将指向自身。这就是导致无限循环的原因。

所以在你的算法中,如果你达到了callee === callee.caller的点,你需要中断,这样才不会发生这种情况。

票数 4
EN

Stack Overflow用户

发布于 2013-03-23 09:39:35

您需要将调用者添加到数组中,并检查下一个调用者是否在数组中。如果是这样,那么你就有了一个循环或递归的调用结构。试试这个:

代码语言:javascript
运行
复制
var args = arguments;
var callee = args.callee;
var caller = callee.caller;

var stack = [callee];

while (caller) {
    if (stack.indexOf(caller) < 0) {
        stack.push(caller);
        args = caller.arguments;
        callee = args.callee;
        caller = callee.caller;
    } else break;
}

console.log(stack);
票数 1
EN

Stack Overflow用户

发布于 2013-05-17 20:45:11

我已经找到了一种将状态信息(在本例中为var .ua (Object) )传递到递归函数的callstack中的方法;

而不是

代码语言:javascript
运行
复制
var fwa.animatedJavascriptControlCenter = {
    ...
    ,
    scanElements : function (el, scanResult) { 
    // a function that walks the DOM tree to find elements that require molding by my js framework
    if (!scanResult) scanResult={};
    if (el.tagName.toLowerCase()!='svg') {
        if (conditions_for_this_element_met(el)) {
            scanResults[el.id] = el;
        };
        if (el.children.length>0) {
            for (var i=0; i < el.children.length; i++) {
                if (el.children[i].tagName.toUpperCase()!=='IFRAME') {
                    scanResult = fwa.animatedJavascriptControlCenter.scanElements(el.children[i], scanResult);
                }
            }
        }
    }
    return scanResult;
    },
    ...
}

执行以下操作:

代码语言:javascript
运行
复制
var fwa.animatedJavascriptControlCenter = {
    ...
    ,
    scanElements : function (el, scanResult) { 
        if (!scanResult) scanResult={};
        if (el.tagName.toLowerCase()!='svg') {
            if (conditions_for_this_element_met(el)) {
                scanResults[el.id] = el;
            }
            if (el.children.length>0) {
                for (var i=0; i < el.children.length; i++) {
                    if (el.children[i].tagName.toUpperCase()!=='IFRAME') {
                        var args = [el.children[i], scanResult];
                        args.ua= tracer.findUA(arguments);
                        var passUAfunc = function(scanResult) {
                            return fwa.animatedJavascriptControlCenter.scanElements(el.children[i], scanResult);
                        }
                        passUAfunc.ua = args.ua;
                        scanResult = passUAfunc(scanResult);
                    }
                }
            }
        }
        return scanResult;
    },
    ...
}

要实现这一点,您还需要:

代码语言:javascript
运行
复制
// Bonus : ;-)
// Enable the passage of the 'this' object through the JavaScript timers
// thanks to https://developer.mozilla.org/en/docs/DOM/window.setTimeout#Callback_arguments
var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
    var 
    oThis = this, 
    ua = tracer.findUA(arguments),
    aArgs = Array.prototype.slice.call(arguments, 2);
    if (ua) aArgs.ua = ua;
    //if (!ua) debugger;
    return __nativeST__(vCallback instanceof Function ? function () {
        vCallback.apply(oThis, aArgs);
    } : vCallback, nDelay);
};
window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
    var 
    oThis = this, 
    aArgs = Array.prototype.slice.call(arguments, 2),
    ua = tracer.findUA(arguments);
    if (ua) aArgs.ua = ua;
    return __nativeSI__(vCallback instanceof Function ? function () {
        vCallback.apply(oThis, aArgs);
    } : vCallback, nDelay);
};

var tracer = {
/*  object last modified : 14 May 2013, 04:46 CEST

    original code by http://javascriptweblog.wordpress.com/2010/06/01/a-tracer-utility-in-2kb/

    modified by rene7705@gmail.com with:
    (1) http://stackoverflow.com/a/15582432/2379929

    augmented by rene7705@gmail with (among other things):
    http://stacktracejs.com/

    this code is now used in and offered as part of the web-framework at http://fancywebapps.com
    (component name : hipLog, to be released later)
*/  


    nativeCodeEx: /\[native code\]/,
    tracing: [],
    traced : [],
    userActions : [],

    findUA : function (arg) {
        var p = arg;
        if (p.ua) return p.ua;

        var callee = arg.callee;
        var caller = callee.caller;

        var stack = [callee];

        while (caller) {
            if (stack.indexOf(caller) < 0) {
                stack.push(caller);
                args = caller.arguments;
                callee = args.callee;
                caller = callee.caller;
            } else break;
        }


        while (p = stack.shift()) {
            if (p.ua) return p.ua;
            if (p.arguments && p.arguments.ua) return p.arguments.ua;

        };
        return false;
    },

    traceMe: function(func, methodName, path) {
        var traceOn = function() {
            var 
            ua = tracer.findUA(arguments),
            startTime = +new Date;

            if (!ua){
                //debugger;
                //ua = tracer.findUA(arguments);
            };
            //if (path=='fwa.animatedJavascriptControlCenter.scanElements') debugger;

            if (!ua) {
                //debugger;
                var
                uaIdx = tracer.userActions.length,
                ua = tracer.userActions[uaIdx] = {
                    uaIdx : uaIdx,
                    startTime : startTime,
                    path : path,
                    stackLevel : 0
                };
                tracer.traced[uaIdx] = [];
            } else {
                var uaIdx = ua.uaIdx;
                ua.stackLevel++;
            }

            arguments.ua = ua;

            var idx = tracer.traced[uaIdx].length;
            tracer.traced[uaIdx][idx] = {
                path : path,
                arguments : arguments
            }; 

            var result = func.apply(this, arguments);

            tracer.traced[uaIdx][idx].stacktrace = printStackTrace() // see http://stacktracejs.com, free, quite useful
            tracer.traced[uaIdx][idx].result = result;
            tracer.traced[uaIdx][idx].timed = new Date - startTime;  
            tracer.traced[uaIdx][idx].stackLevel = ua.stackLevel;
            ua.stackLevel--;
            return result;
        };
        traceOn.traceOff = func;
        for (var prop in func) {
            traceOn[prop] = func[prop];
        }
        console.log("tracing " + path);
        return traceOn;
    },

    traceAll: function(root, path, recurse) {
        if ((root == window) || !((typeof root == 'object') || (typeof root == 'function'))) {return;}
        for (var key in root) {
            if ((root.hasOwnProperty(key)) && (root[key] != root)) {
                var thisObj = root[key];
                if (typeof thisObj == 'function') {
                    if ((this != root) && !thisObj.traceOff && !this.nativeCodeEx.test(thisObj)) {
                        root[key] = this.traceMe(root[key], key, path+'.'+key);
                        this.tracing.push({obj:root,methodName:key, path:path+'.'+key});
                    }
                }
                recurse && this.traceAll(thisObj, path+'.'+key, true);
             }
        }
    },

    untraceAll: function() {
        for (var i=0; i<this.tracing.length; ++i) {
            var thisTracing = this.tracing[i];
            thisTracing.obj[thisTracing.methodName] =
                thisTracing.obj[thisTracing.methodName].traceOff;
        }
        //console.log("tracing disabled");
        tracer.tracing = [];
    }
}

用法如下:

代码语言:javascript
运行
复制
// call this just after _completely_ defining yourFrameworkRootObject and yourSiteCodeRootObject
tracer.traceAll (yourFrameworkRootObject, true);
tracer.traceAll (yourSiteCodeRootObject, true);

仅供参考:这段代码摘自我的js框架http://fancywebapps.com,将成为它的hipLog组件的一部分,该组件旨在向您显示任何userAction的完整执行路径(有或没有到服务器的往返和/或setTimeout()),如点击、悬停或页面加载。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15582309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档