1

A script was needed to quickly tell me how many html comments there are on the page and what their contents are. Using an anonymous function for recursive DOM traversal seemed appropriate:

var comments = []; //set up an array where comment contents will be copied to

(function(D) {
  if (8===D.nodeType) comments.push(D.nodeValue); //check if node is a comment
  D=D.firstChild;
  while (D) {
    arguments.callee(D); //recursively look for comments...
    D=D.nextSibling; //...and remember to iterate over all children of any node
  }
})(document);

console.log(comments.join("\r\n")); //list all comments

Fiddle works as expected, but I was curious if it was really the same function called over and over, or were there multiple references to the original function called, or were there multiple identical functions called... After all, there has been no named reference made, so how would it work as the traversal goes deeper? I thought I may be able to check that by adding the following code into while (D) {...}

//tmpCallee has been declared
if (tmpCallee) {
  console.warn(arguments.callee === tmpCallee);//true
  /*great, means these should be both pointing to the same function*/
  console.log(arguments.callee === arguments.caller);//false
  /*wait, what? didn't we just establish above that 
    all of our functions called recursively would be the same?*/
  console.log(arguments.caller);//undefined... but it was called recursively!
  console.log(arguments.callee);//prints our function code verbatim as it should
}
tmpCallee = arguments.callee;

I'm confused. 1) am I really calling the same function over and over or are there multiple identical functions called or is something else at play? 2) why does arguments.caller not point at our function? it was clearly invoked by it - that's how recursion works, no?

Oleg
  • 24,465
  • 8
  • 61
  • 91
  • 3
    You could have just named the function: `(function fn (D) {`, and then `while (D) { fn(D); ...`. No need for the **deprecated** `arguments.callee`... – Šime Vidas Sep 18 '12 at 12:35
  • As Šime Vidas said. Just wanted to add that `arguments.callee` doesn't work in strict mode. – some Sep 18 '12 at 12:46
  • I'm not arguing that a named function would be a preferred option, but what is the logic behind this particular behaviour? I can't find any documentation and it seems *really really weird* - hence the question – Oleg Sep 18 '12 at 12:48
  • arguments do not have any caller property.you should use arguments.callee.caller instead. – Anoop Sep 18 '12 at 12:53

3 Answers3

1

am I really calling the same function over and over or are there multiple identical functions called or is something else at play?

Yes, you have only one function instance, which you refer to all the time. However, you are setting up a call stack in which local variables (in your case the argument D) would be saved for every invocation.

why does arguments.caller not point at our function?

There is no caller property on the arguments object, it was removed. You probably meant the caller property of the function object, which is non-standard but still usable (although forbidden in strict mode as well as argments.callee).

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks for correcting me, `arguments.callee === arguments.callee.caller` returns true and everything is back to normal! – Oleg Sep 18 '12 at 21:44
1

There is only one function instance involved here. That function is calling itself recursively. You can easily check this by assigning a name to the function expression in your code:

(function fn (D) {

and then, inside the body:

fn === arguments.callee // => true

The above will be true for every invocation, showing that only one function was created and invoked in this process.

Also, this:

arguments.callee === arguments.callee.caller // => true, except first time

shows you that the function invokes itself, i.e. the caller is the callee. The above expression is true for every invocation, except the first one, since the first invocation occurred from global code.

Live demo: http://jsfiddle.net/HbThh/2/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • Of course this is a lot more clear/readable when the function is named; the incorrect `arguments.callee === arguments.caller` simply misled me into thinking the mechanics would be different for anonymous functions – Oleg Sep 18 '12 at 21:48
0

There is no caller property for arguments you should use arguments.callee.caller to get caller.

Anoop
  • 23,044
  • 10
  • 62
  • 76