web-dev-qa-db-ja.com

arguments.callee.callerプロパティがJavaScriptで非推奨になったのはなぜですか?

JavaScriptでarguments.callee.callerプロパティが非推奨になったのはなぜですか?

追加され、JavaScriptで廃止されましたが、ECMAScriptによって完全に省略されました。一部のブラウザ(Mozilla、IE)は常にこれをサポートしており、サポートを削除する予定はありません。その他(Safari、Opera)はサポートを採用していますが、古いブラウザーでのサポートは信頼できません。

この貴重な機能をリンボに入れる正当な理由はありますか?

(または、呼び出し元の関数のハンドルを取得するより良い方法はありますか?)

209
pcorcoran

JavaScriptの初期バージョンでは、名前付き関数式が許可されていなかったため、再帰的な関数式を作成できませんでした。

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


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

これを回避するために、次のことができるようにarguments.calleeが追加されました。

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

しかし、これは実際には非常に悪い解決策でした。これは(他の引数、呼び出し先、呼び出し元の問題と組み合わせて)一般的なケースではインライン化とテール再帰を不可能にします(トレースなどを通じて特定のケースで達成できますが、最高のコードでも他の方法では必要ないチェックのため、最適ではありません)。もう1つの大きな問題は、再帰呼び出しが異なるthis値を取得することです。次に例を示します。

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は、次のような名前付き関数式を許可することでこれらの問題を解決しました。

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

これには多くの利点があります。

  • この関数は、コード内から他の関数​​と同様に呼び出すことができます。

  • 名前空間を汚染することはありません。

  • thisの値は変更されません。

  • より高性能です( 引数オブジェクト へのアクセスは高価です)。

おっと、

他のすべてに加えて、質問はarguments.callee.caller、またはより具体的には Function.caller であることに気付いただけです。

いつでもスタック上の関数の最も深い呼び出し元を見つけることができます。上記で述べたように、呼び出しスタックを見ると、1つの大きな効果があります。それは、多数の最適化を不可能にするか、はるかに難しくします。

例えば。関数fが未知の関数を呼び出さないことを保証できない場合、fをインライン化することはできません。基本的に、それは些細なことであるかもしれないどんな呼び出しサイトでも多くの警備員を集めることを意味します:

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

Jsインタープリターが、呼び出しが行われた時点で提供されたすべての引数が数値であることを保証できない場合、インラインコードの前にすべての引数のチェックを挿入するか、関数をインライン化できません。

この特定のケースでは、スマートインタープリターはチェックをより最適なものに再配置し、使用されない値をチェックしないようにする必要があります。ただし、多くの場合、それは不可能なため、インライン化できなくなります。

248
olliej

arguments.callee.callerは じゃない Function.caller プロパティを使用しますが、非推奨です。 ( arguments.callee は現在の関数への参照を提供するだけです)

  • Function.callerは、ECMA3によると非標準ですが、 現在のすべての主要なブラウザ
  • arguments.caller  Function.callerの代わりに廃止され、現在の主要なブラウザ(Firefox 3など)には実装されていません。

したがって、状況は理想的ではありませんが、すべての主要なブラウザーでJavascriptの呼び出し関数にアクセスする場合は、名前付き関数参照で直接アクセスするか、Function.callerプロパティを介して匿名関数からアクセスするarguments.calleeプロパティを使用できます。

89
James Wheare

Arguments.calleeよりも、名前付き関数を使用することをお勧めします。

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

よりも良い

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

指定された関数は、callerプロパティを介して呼び出し元にアクセスできます。

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

より良い

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

この廃止は、現在のECMAScript 設計原則 によるものです。

29
Zach

その名前をハードコードすることなく、関数を参照するための引数がまだあります。

15
ardsasd

ただの拡張。 「this」の値は、再帰中に変化します。次の(変更された)例では、factorialは{foo:true}オブジェクトを取得します。

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

最初に呼び出される階乗はオブジェクトを取得しますが、これは再帰呼び出しには当てはまりません。

0
FERcsI