arguments.callee

概要

現在実行している関数を示します。

arguments のプロパティ
実装されたバージョン JavaScript 1.2
JavaScript 1.4: Function.arguments のプロパティとしての callee を非推奨とする。関数ローカルの arguments 変数のプロパティとしてはそのまま維持。
ECMAScript エディション ECMA-262

説明

callee はすべての関数オブジェクト内で利用できるローカル変数 arguments のプロパティです。Function.arguments のプロパティとしての callee はもはや使用されていません (Function.arguments 自体も非推奨)。

arguments.callee によって無名関数は自分自身を参照することが出来ます。これは無名再帰関数を作成するときに必要になります。

this キーワードは現在実行している関数を参照するものではありません。関数本体の内部でその関数を参照するには callee プロパティを使用してください。

Note: You should avoid using arguments.callee() and just give every function (expression) a name.
Warning: The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode.

例: 無名再帰関数内での arguments.callee の使用

再帰関数は自分自身を参照する必要があります。関数が自分自身を参照するには、一般的には関数の名前を使用します。しかしながら、無名関数には名前がありません。さらにその無名関数を参照するアクセス可能な変数も無い (関数がどの変数にも代入されていない) 場合、その関数には自分自身を参照する手段がありません。 (無名関数は関数式または Function コンストラクタによって作成できます) そこで arguments.callee の出番です。

次の例では関数を定義し、その関数内でさらに階乗関数を定義し、それを返しています。

function makeFactorialFunc() {
   alert('making a factorial function!');
   return function(x) {
      if (x <= 1) return 1;
      return x * arguments.callee(x - 1);
   };
}

var result = makeFactorialFunc()(5); // 120 (5 * 4 * 3 * 2 * 1) を返す

A bug after the deprecation of arguments.callee?

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

For example, this syntax worked:

function factorial (n) {
    return !(n > 1) ? 1 : factorial(n - 1) * n;
}

[1,2,3,4,5].map(factorial);

but:

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

did 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();

ECMAScript 3 resolved these issues by allowing named function expressions. For example:

[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)

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 optimizations impossible, or much much more difficult. For example, 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 JavaScript 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.

However in a case like the following there are not alternatives to arguments.callee, so its deprecation could be a bug (see バグ 725398):

function createPerson (sIdentity) {
    var oPerson = new Function("alert(arguments.callee.identity);");
    oPerson.identity = sIdentity;
    return oPerson;
}

var john = createPerson("John Smith");

john();

ドキュメントのタグと貢献者

最終更新者: teoli,