この記事は 初心者が見ると混乱する要因になるので
初心者が見る資料 としては不適切です。
この特殊な条件でしか発生しないので通常のコーディングでは考慮する必要は無いです。
特に引数と arguments を 両用するのはとてもお勧めできません。
eslint の prefer-rest-params
を有効にすることをおすすめします。
arguments と引数
言語的に参照渡しの無い言語である javascript には 参照渡しでいうところの 参照………つまり オブジェクト参照ではなく、いわゆる 変数のエイリアスにあたる動作は存在しない………と思っていたが、よくよく思っていたら存在したのでメモする。
具体的には次の様なコード
function func(arg1){
console.log('step1: ', arg1 === arguments[0]);
arg1 = {};
console.log('step2: ', arg1 === arguments[0]);
arguments[0] = {};
console.log('step3: ', arg1 === arguments[0]);
}
console.log('test: ', {} === {});
func({});
この実行結果は
test: false
step1: true
step2: true
step3: true
となるつまり、 第一引数 は arguments[0]
の参照(エイリアス)と言えるのである。
上記の例がわかりにくいというのであれば次の様な表現でもいいと思います。
function func(arg1){
console.log('step1: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arg1 = "step2";
console.log('step2: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arguments[0] = "step3";
console.log('step3: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
}
func("step1");
この実行結果は次の様になります。
step1: true ('step1', 'step1')
step2: true ('step2', 'step2')
step3: true ('step3', 'step3')
仕様書では
この動作について仕様書 を読んだところ NOTE 2 として次の説明があった。
NOTE2
The integer-indexed data properties of an arguments exotic object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function's execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. If the arguments object is an ordinary object, the values of its properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.
10.4.4 Arguments Exotic Objects - ECMAScript® 2025 Language Specification
とあり、 パラメータ数 に一致する index の位置にあたるものに関しては共有するとあります。つまり arguments が主となり共有している様なので次の様な実験を行うことを考えます。
function func(arg1) {
console.log('deletable: %o', delete arg1);
console.log('step1: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arg1 = "step2";
console.log('step2: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arguments[0] = "step3";
console.log('step3: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
}
func("step1");
function func(arg1) {
console.log('deletable: %o', delete arguments[0]);
console.log('step1: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arg1 = "step2";
console.log('step2: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
arguments[0] = "step3";
console.log('step3: %o (%o, %o)', arg1 === arguments[0], arg1, arguments[0]);
}
func("step1");
するとそれぞれ次の様な実行結果となります。
deletable: false
step1: true ('step1', 'step1')
step2: true ('step2', 'step2')
step3: true ('step3', 'step3')
deletable: true
step1: false ('step1', undefined)
step1: false ('step2', undefined)
step1: false ('step2', 'step3')
つまり arguments[n] を削除すると 引数との参照を切ることができるのです。
以上。