假设我想获得某种堆栈跟踪,获取在当前函数之前调用的所有函数的名称。
我这样做:
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);
大部分时间都可以正常使用。但有时它会陷入无限循环,我想知道:这怎么可能?我能做些什么呢?
这是我的无限循环的输出:
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
答案 0 :(得分:4)
arguments.callee.caller
指向函数引用,它在每个调用堆栈中只存在每个函数之一。
每次调用一个函数时,caller
属性都会被设置,这意味着如果在调用堆栈中多次调用相同的函数,就像递归函数一样,前一个值将被重置,而caller
现在指向自己。这就是导致无限循环的原因。
所以在你的算法中,如果你达到callee === callee.caller
你需要打破的点,以免发生这种情况。
答案 1 :(得分:1)
您需要在数组中添加调用者并检查下一个调用者是否在数组中。如果是这样,那么你有一个循环或递归调用结构。试试这个:
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);
答案 2 :(得分:0)
我找到了一种方法将状态信息(在这种情况下为var .ua(Object))传递给递归函数的callstack;
而不是
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;
},
...
}
做的:
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;
},
...
}
要完成这项工作,您还需要:
// 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 = [];
}
}
用作:
// call this just after _completely_ defining yourFrameworkRootObject and yourSiteCodeRootObject
tracer.traceAll (yourFrameworkRootObject, true);
tracer.traceAll (yourSiteCodeRootObject, true);
仅供参考:此代码来自我的js框架http://fancywebapps.com,并且将成为它的hipLog组件的一部分,旨在向您显示完整的执行路径(有或没有到服务器的往返)和/或setTimeout()s)任何userAction,如点击,悬停或页面加载。