JavaScriptのargumentsはなかなか不思議なオブジェクトで、配列のようで配列でなかったり、関数スコープで暗黙に作られていたり、きわめつけは…
function hoge(a, b) { console.log(a); arguments[0] = 5; console.log(a); } hoge(1, 2);
1 5
!?
function fuga(a, b) { console.log(arguments[0]); a = 7; console.log(arguments[0]); } fuga(1, 2);
1 7
!!??
arguments の値と仮引数の値が連動するという摩訶不思議な挙動を示します。
何故このような挙動をするのか、 ECMA-262 5th 仕様書を紐解いてみました。10章6節 Arguments Object によると…
- Add name as an element of the list mappedNames.
- Let g be the result of calling the MakeArgGetter abstract operation with arguments name and env.
- Let p be the result of calling the MakeArgSetter abstract operation with arguments name and env.
- Call the DefineOwnProperty internal method of map passing ToString(indx), the Property Descriptor {Set: p, Get: g, Configurable: true}, and false as arguments.
argumentsオブジェクトへの数値インデックスアクセスに対して、setter/getterが定義されているようです。JavaScriptコードで表すと以下のようなイメージになります。
var a, b; var arguments = Object.defineProperties(new Arguments(), { "0" : { get: function() { return a; }, set: function(val) { a = val; } }, "1" : { get: function() { return b; }, set: function(val) { b = val; } } });
なお、ECMA-262 3rd Editionでは、Activation Objectという見えないオブジェクトが半ば強引に定義され、仮引数やargumentsオブジェクトはActivation Objectのプロパティとされていました。5th Editionでは不自然なActivation Objectは削除され、getter/setterを使って同様の動作をするように定義しなおされています。