假设我想要获取某种堆栈跟踪,获取在当前函数之前调用的所有函数的名称。
我这样做:
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发布于 2013-03-23 10:02:29
arguments.callee.caller指向函数引用,它只存在于每个调用堆栈中的每个函数中的一个。
每次调用函数时,都会设置caller属性,这意味着如果同一函数在调用堆栈中被多次调用(递归函数会发生这种情况),则先前的值将被重置,并且caller现在将指向自身。这就是导致无限循环的原因。
所以在你的算法中,如果你达到了callee === callee.caller的点,你需要中断,这样才不会发生这种情况。
发布于 2013-03-23 09:39:35
您需要将调用者添加到数组中,并检查下一个调用者是否在数组中。如果是这样,那么你就有了一个循环或递归的调用结构。试试这个:
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);发布于 2013-05-17 20:45:11
我已经找到了一种将状态信息(在本例中为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组件的一部分,该组件旨在向您显示任何userAction的完整执行路径(有或没有到服务器的往返和/或setTimeout()),如点击、悬停或页面加载。
https://stackoverflow.com/questions/15582309
复制相似问题