JavaScript | arguments の内部挙動

JavaScript JavaScript
スポンサーリンク

arguments の「内部でどう動いているか」は、見た目より少し複雑で、実装や ECMAScript(仕様)のルールに依存します。ここでは 初心者〜中級者が知っておくと役立つレベル に噛み砕いて、実例と仕様的な要点を混ぜて説明します。


1. 高レベルのイメージ(まずは直感)

  • arguments は「配列っぽく見えるオブジェクト(array-like)」で、length と数値プロパティ(0,1,2,...)を持ちますが 本物の Array ではない
  • 古い(非 strict)関数 では、関数の 個別の引数変数(例:a)と arguments[n] が“つながっている(alias)”。片方を変えるともう片方に反映されます。
  • strict mode や「特定のパラメータ構文(default, destructuring, rest を含むなど)」ではそのリンクは切られ、arguments とパラメータは 別物 です。

2. 仕様用語(軽く触れる)

ECMAScript の仕様では、argumentsArguments Exotic Object として扱われます。内部的には [[ParameterMap]] のような仕掛けで「引数名と arguments の数値プロパティを結びつける」ことができます。
ポイント:

  • [[ParameterMap]] がある場合:arguments[0] と最初のパラメータはリンク(同期)する。
  • [[ParameterMap]] がない場合:リンクは存在しない(=別物)。

(実装詳細はエンジンごとに最適化されるため、エンジン内部での実体はさらに複雑です。)


3. 具体的な挙動例と説明

A — 通常(非 strict)での同期(alias)

function demo(a, b) {
  console.log(arguments[0]); // 1
  a = 99;
  console.log(arguments[0]); // 99 ← a を変えると arguments[0] も変わる
  arguments[1] = 77;
  console.log(b);            // 77 ← arguments を変えると b も変わる
}
demo(1, 2);
JavaScript

説明:この場合は [[ParameterMap]] が働いていて、パラメータ変数と arguments のプロパティが相互に反映されます。


B — strict mode(同期しない)

'use strict';
function demo(a, b) {
  console.log(arguments[0]); // 1
  a = 99;
  console.log(arguments[0]); // 1 ← a を変えても arguments[0] は変わらない
  arguments[1] = 77;
  console.log(b);            // 2 ← arguments を変えても b は変わらない
}
demo(1, 2);
JavaScript

説明:strict ではリンクが切れており、それぞれ独立しています。これが現在の推奨的な安全な振る舞いです。


C — デフォルト引数や分割代入があるとき(ES2015以降)

ES2015 以降、「単純なパラメータリスト(simple parameter list)でない」 場合、パラメータと arguments のマッピングが作られない仕様になりました。例:

function demo(a = 1) {        // default parameter を含むため "simple" ではない
  console.log(arguments[0]); // 10
  a = 99;
  console.log(arguments[0]); // 10 ← 変わらない(同期しない)
}
demo(10);
JavaScript

ポイントa = 1 のような default、または引数の destructuring を使うと、たとえ非 strict でも arguments と同期しない場合があります。


D — delete arguments[0] の効果(削除するとマッピングが消える)

非 strict で同期がある場合でも、delete arguments[0] を実行するとその arguments のプロパティが消え、対応するマッピング([[ParameterMap]] のエントリ)も消えるため 以後は同期しなくなることがあります。つまりマッピングは「動的に壊せる」ことがあります(仕様上の細かい動きに依存します)。

例イメージ:

function demo(a) {
  console.log(a, arguments[0]); // 1 1
  delete arguments[0];
  a = 99;
  console.log(a, arguments[0]); // 99 undefined  ← 以後同期しない
}
demo(1);
JavaScript

4. arguments.callee / arguments.caller など

  • arguments.callee(関数自身を参照する)は非推奨で、strict mode では使えません(エラーになる)。
  • これらは古いスタイル(関数式の自己参照)用の機能で、現在は function name() {} や関数式に名前を付ける、あるいは変数で参照する方が推奨されます。

5. 実装と最適化の観点(エンジン内部)

  • モダンな JavaScript エンジン(V8、SpiderMonkey、Chakra など)はパフォーマンス最適化を行うため、arguments マッピングを必要なときだけ作ったり、あるいは内部的に異なる表現に変換したりします。
  • つまり、arguments を頻繁に使うコードは最適化を阻害することがあるため、パフォーマンスの観点からも ...rest を使うことが推奨されます。

6. 要点まとめ

  • arguments は array-like なオブジェクトで、時と場合によりパラメータと「同期する/しない」がある。
  • 同期(alias)は非 strict かつ「simple parameter list」のときに成り立つ。
  • strict mode、arrow 関数、default/ destructuring を使うと同期は無くなる。
  • delete arguments[n] などでマッピングを壊すことができる(仕様上の挙動)。
  • 実装は最適化されうるので、可読性と性能のために ...rest(rest パラメータ)を使うのが現代的で安全

7. おまけ:よくある「試したら違った」ケース

  • 「非 strict なのに同期してない」 → たいてい default 引数や destructuring がある か、あるいは試している関数がモジュール(<script type="module"> は strict)であることが原因です。
  • 「arrow function で arguments が見つからない」 → arrow は arguments を持たない(周囲の通常関数の arguments を参照するわけでもない)ためです。
タイトルとURLをコピーしました