arguments.callee.callerプロパティがJavaScriptで廃止されたのはなぜですか?


214

arguments.callee.callerプロパティがJavaScriptで廃止されたのはなぜですか?

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

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

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


2
広範囲に使用されるわずかな機能を取得する機能は、他のブラウザの互換性のバグになるため、他のブラウザでサポートされています。サイトが1つのブラウザーにのみ存在する機能を使用している場合、そのサイトは他のすべてのブラウザーでは機能せず、通常、ユーザーはブラウザーが機能しないと考えます。
olliej 2008年

4
(ほとんどすべてのブラウザでこれが一度に行われます。たとえば、この機能(およびJS自体)はNetscape、XHRはIEで作成され、CanvasはSafariなどで使用されます。これらのいくつかは便利で、他のブラウザで採用されています。時間をかけて(すべての例ですXHR JS、キャンバス、)、いくつかの(.callee)ではありません。
olliej

@olliejそれが使用されているためであり、標準であるためではなく(または標準で非推奨となっているにもかかわらず)、それをサポートすることについてのあなたのコメントは非常に真実です!これが、私が基準を役立たないと感じているときはいつでも、ほとんど基準を無視し始めた理由です。開発者としての私たちは、仕様が私たちがすべきだと言っていることではなく、機能することを使用することによって、標準の方向性を形作ることができます。これは、我々が得た方法である<b><i>(はい、それらはある時点で廃止され)バック。
Stijn de Witt

回答:


252

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;
 });

ただし、これは実際に非常に悪い解決策でした(他の引数、呼び出し先、および呼び出し元の問題と組み合わせて)、インライン化と末尾再帰が一般的なケースで不可能になります(トレースなどを通じて特定のケースで実現できますが、最高のコードでも可能です)他の方法では必要とされないチェックのため、最適ではありません)。その他の主な問題は、再帰呼び出しが異なる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オブジェクトへのアクセスは高価です)。

おっと、

他のすべてのものに加えて、問題がについてarguments.callee.callerであるか、より具体的にであることがわかりましたFunction.caller

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

例えば。関数fが未知の関数を呼び出さないことを保証できない場合、インライン化することはできませんf。基本的に、それは、自明のことではなかった可能性がある呼び出しサイトが多数のガードを蓄積することを意味します。

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

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

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


12
最適化が難しいという理由だけで価格が下がっているということですか?それはちょっとばかげています。
Thomas Eding、2010

11
いいえ、私はいくつかの理由を挙げましたが、それに加えて最適化が困難です(一般に、歴史上、最適化が難しいものは人々がフォローするのが難しいセマンティクスを持っていることが示されています)
olliej

17
この引数は、それが重要だ場合、その値は呼び出しで設定することができ、ビットスプリアスです。通常は使用されません(少なくとも、再帰関数では問題が発生していません)。名前で関数を呼び出すことには同じ問題があるthisので、呼び出し先が良いか悪いかは関係ないと思います。また、calleecallerはストリクトモード(ECMAscript ed 5、Dec 2009)でのみ「非推奨」ですが、olliejが投稿した2008年には知られていないと思います。
RobG、2011年

8
)私はまだ論理がわかりません。ファーストクラスの関数を使用するどの言語でも、iを知らなくても自分自身を参照できる関数本体を定義できることには明らかな価値があります
Mark Reed

8
RobGはこれを指摘しましたが、それほど明確ではなかったと思います。名前付き関数を使用して再帰すると、グローバルスコープのthisif の値のみが保持されthisます。それ以外の場合はすべて、最初の再帰呼び出しの後に値this 変化するので、の保持についてほのめかした答えの部分はthis実際には有効ではないと思います。
JLRishe 2014年

89

arguments.callee.callerプロパティは使用されますが、非推奨ではありません。(現在の関数への参照を提供するだけです)Function.callerarguments.callee

  • Function.callerECMA3によれば非標準ですが、現在のすべての主要なブラウザに実装されています
  • arguments.caller 非推奨であり、現在の主要なブラウザ(Firefox 3など)には実装されていません。Function.caller

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


5
これは、非推奨の機能と非推奨の機能の最も良い説明であり、非常に便利です。Function.callerが実行できない(再帰的な関数のスタックトレースを取得する)の良い例については、developer.mozilla.org
Juan Mendes

1
ただし、arguments.callee厳格モードでは禁止されています。私も悲しくなりましたが、もう使わない方がいいです。
Gras Double、

1
MDNへのarguments.calleeハイパーリンクは、strictモードで削除されることを示しています。それは非推奨と同じではありませんか?
スタイル

1
arguments.callee.callerES5ストリクトモードでは非推奨であることに注意してください:「非推奨になったもう1つの機能は、arguments.callee.caller、より具体的にはFunction.callerでした。」(出典
thdoan

29

arguments.calleeよりも名前付き関数を使用する方が良いです。

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

よりも良い

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

名前付き関数は、callerプロパティを通じて呼び出し元にアクセスできます。

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

それはより良いです

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

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


2
名前付き関数を使用する方が良い理由を説明してください。匿名関数で呼び出し先を使用する必要はありませんか?
AnthonyWJones 2008

27
匿名関数で呼び出し先を使用している場合、匿名であってはならない関数があります。
Prestaul

3
デバッグする最も簡単な方法は、.caller()を使用することです。そのような場合、名前付き関数は役に立ちません-どの関数が呼び出しを行っているかを解明しようとしています。
SamGoody

6
より良い定義。たとえば、arguments.callee が機能している間、IE6-8には関数の癖があります。
cmc

1
IE6-8の癖とは別に、コードは密結合されます。オブジェクトまたは関数、あるいはその両方の名前がハードコーディングされている場合、ardsasdおよびrsk82で言及されているように、コードベースのサイズが大きくなると、リファクタリングの危険が大きくなります。単体テストは防衛線であり、私はそれらを使用していますが、それでも、このハードコーディングの問題に関して個人的に本当に満足できる答えではありません。
Jasmine Hegman、2013年

0

単なる拡張です。「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}     );

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


1
あなたがそれを間違っているからです。this維持する必要がある場合は、factorial.call(this, n-1)再帰的なコードの記述で実際に見つけたものを記述します。通常、this、またはthisツリー内のいくつかのノードを参照し、それが変化していること実際にそれの良いです。
Stijn de Witt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.