views:

8254

answers:

5

Why was the arguments.callee.caller property deprecated in JavaScript?

It was added and then deprecated in JavaScript, but it was omitted altogether by ECMAScript. Some browser (Mozilla, IE) have always supported it and don't have any plans on the map to remove support. Others (Safari, Opera) have adopted support for it, but support on older browsers is unreliable.

Is there a good reason to put this valuable functionality in limbo?

(Or alternately, is there a better way to grab a handle on the calling function?)

A: 

I think you want arguments.callee.

matt b
+10  A: 

It is better to use named functions than arguments.callee:

 function foo () {
     ... foo() ...
 }

is better than

 function () {
     ... arguments.callee() ...
 }

The named function will have access to its caller through the caller property:

 function foo () {
     alert(foo.caller);
 }

which is better than

 function foo () {
     alert(arguments.callee.caller);
 }

The deprecation is due to current ECMAScript design principles.

Zach
Can you describe why using the named function is better. Is there never a need to use callee in an anonymous function?
AnthonyWJones
If you are using callee in an anonymous function, then you have a function that should not be anonymous.
Prestaul
+26  A: 

Early versions of javascript did not allow named function expressions, and for this reason you could not make a recursive function expression.

eg.

 function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 }
 [1,2,3,4,5].map(factorial);

this works, but

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
 });

does not. To get around this arguments.callee was added so you could do

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

However this was actually a really bad solution as this (in conjunction with other arguments, callee, and caller issues) make inlining and tail recursion impossible in the general case (you can achieve it in select cases through tracing etc, but even the best code is sub optimal due to checks that would not otherwise be necessary). The other major issue is that the recursive call will get a different this value, eg.

var global = this;
var sillyFunction = function (recursed) {
    if (!recursed)
        return arguments.callee(true);
    if (this !== global)
        alert("This is: " + this);
    else
        alert("This is the global");
}
sillyFunction();

Anyhoo, EcmaScript 3 resolved this issues by allowing named function expressions, eg.

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

This has numerous benefits

  • the function can be called like any other from inside your code
  • it does not pollute the namespace
  • the value of this does not change
  • it's more performant (accessing the arguments object is expensive)

Whoops, just realised that in addition to everything else the question was about arguments.callee.caller, or more specifically Function.caller. At any point in time you can find the deepest caller of any function on the stack, and as I said above looking at the call stack has one single major effect: It makes a large number of optimisations impossible, or much much more difficult. Eg. if you cannot guarantee that a function f will not call an unknown function it is not possible to inline f. Basically it means that any call site that may have been trivially inlinable accumulates a large number of guards, take:

 function f(a,b,c,d,e) { return a ? b * c : d * e; }

If the js interpreter cannot guarantee that all the provided arguments are numbers at the point that the call is made, it needs to either insert checks for all the arguments before the inlined code, or it cannot inline the function. Now in this particular case a smart interpreter should be able to rearrange the checks to be more optimal and not check any values that would not be used. However in many cases that's just not possible and therefore it becomes impossible to inline.

olliej
Are you saying it is depricated just because it's hard to optimize? That's kinda silly.
trinithis
No, i listed a number of reasons, in addition to it making it hard to optimise (although in general history has shown that things that are hard to optimise also have semantics that people have difficulty following)
olliej
A: 

there is still an argument for referring to the function without having to hard-code its name.

+4  A: 

arguments.callee.caller is not deprecated, though it does make use of the Function.caller property. (arguments.callee will just give you a reference to the current function)

  • Function.caller, though non-standard according to ECMA3, is implemented across all current major browsers.
  • arguments.caller is deprecated in favour of Function.caller, and isn't implemented in some current major browsers (e.g. Firefox 3).

So the situation is less than ideal, but if you want to access the calling function in Javascript across all major browsers, you can use the Function.caller property, either accessed directly on a named function reference, or from within an anonymous function via the arguments.callee property.

James Wheare