◆複雑ですね。

JavaScriptで関数実行中に引数の情報が入るargumentsというのがあります
受け取らなかった引数も含めて関数実行時に送られた引数一覧が配列のようなオブジェクトに入っています
Arrayが継承されていないので配列系メソッドは残念ながら使えません

document.getElementsByClassNameの返り値やdocument.querySelectorAllの返り値のようにNodeListなどのconstructorによって作られていないのでarguments.constructorはObjectになっています
もちろんarguments.__proto__も{}です

配列とは違うのだよ、配列とは

function a(a,b,c,d){ console.log(a,b,c,d,arguments); a = -1; arguments[1] = -2; //b c = -3; arguments[3] = -4; //d console.log(a,b,c,d,arguments); console.log(arguments.length); }

argumentsに[a,b,c,d]の配列(のようなもの)が入っています
aとcでは直接 bとdではargumentsを通して変数を書き換えます

a(1,2,3,4)
1 2 3 4 [1, 2, 3, 4] VM2763:3
-1 -2 -3 -4 [-1, -2, -3, -4] VM2763:8
4

変数を書き換えた時もargumentsを書き換えた時も 書き換えていない方にも影響していますね

普通の配列とプリミティブ値の場合だと下のようになります

配列→プリミティブ値、配列を変更
aa:配列
ap:プリミティブ値
aa = [1]
[1]
ap = aa[0]
1
aa[0] = 2
2
ap
1

配列→プリミティブ値、プリミティブ値変更
aa = [10]
[10]
ap = aa[0]
10
ap = 20
20
aa
[10]

プリミティブ値→配列、配列変更
ap = 100
100
aa = [ap]
[100]
aa[0] = 200
200
ap
100

プリミティブ値→配列、プリミティブ値変更
ap = 1000
1000
aa = [ap]
[1000]
aa[0] = 2000
2000
ap
1000


どの場合も変更したものだけ変わっていて 変更していない方はそのままです
これが普通の動きです
しかし argumentsは特殊なので両方が変わります

function b(a,b){     var aa = a;     console.log(a,aa,b,arguments);     aa = -1;     arguments[1] = -2; //b     console.log(a,aa,b,arguments); }

b(1,2)
1 1 2 [1, 2] VM3425:4
1 -1 -2 [1, -2]

2は-2になるけど1はそのまま


引数が足りないときは

諸事情で先にFirefoxの結果を載せます
a(1,2)
undefined
1 2 undefined undefined Arguments { 0: 1, 1: 2, 他 2 個... }
-1 -2 -3 undefined Arguments { 0: -1, 1: -2, 3: -4, 他 2 個... }
2

a(1,2,3,4)
undefined
1 2 3 4 Arguments { 0: 1, 1: 2, 2: 3, 3: 4, 他 2 個... }
-1 -2 -3 -4 Arguments { 0: -1, 1: -2, 2: -3, 3: -4, 他 2 個... }

a(1,2)の方を見ると-3は変数cの方だけで-4はargumentsだけに入っています
関数実行時にundefinedになっているものは変更しても変更した方しか反映されないようです

Chromeで謎の挙動


これをChromeでやってみると……

a(1,2)
1 2 undefined undefined [1, 2] VM2763:3
-1 -2 -3 undefined [-1, -2, undefined, -4, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined] VM2763:8
2

 
なにこれ!?バグった!!!

-3がcだけで-4がargumentsだけというのは一緒です
一緒なのですがそれ以降がおかしなことになってます
undefinedがいっぱい並んでます

どうしてこうなった!

最後の2はargumentsのlengthプロパティを表示したものなのでundefinedがいっぱいですが長さは2らしいです
デフォルトのargumentsだと2つだけど argumentsに書き込んでいるから値が入っているのは3つです
配列じゃないのでlengthプロパティは自動更新されないようです
これはFirefoxも一緒です
それはいいのですが このundefinedの山 こいつらをどうしてくれようか……

argumentsを書き換えること自体がやるべきことじゃないと思うので想定外のことすると実装依存なのかなということでとりあえず納得する
バージョン36~43で確認してます

argumentsを引数で渡す

function f1(a,b){ console.log("F1",a,b,arguments); f2(arguments); console.log("F1",a,b,arguments); } function f2(args){ args[1] = -1; } function g1(a,b){ console.log("G1",a,b,arguments); g2.apply(null, arguments); console.log("G1",a,b,arguments); } function g2(a,b){ console.log("G2", a,b,arguments); a = -2; arguments[1] = -3; console.log("G2", a,b,arguments); } function h1(a,b){ console.log("H1",a,b,arguments); h2.apply(null, arguments); console.log("H1",a,b,arguments); } function h2(a,b){ console.log("H2", a,b,arguments); a[0] = -2; arguments[1][0] = -3; console.log("H2", a,b,arguments); }

1の関数ではargumentsや引数を変更しません
その代わり引数としてargumentsを2の関数に渡します
2の関数では受け取った1の関数のargumentsに編集をします

f:1ではそのままargumentsを渡して 2で編集する
g:1ではapplyで引数を渡して 2で編集する
h:gと同じだけど参照渡しになる配列をh1に渡す

f1(1,2)
F1 1 2 [1, 2] VM1563:13
F1 1 -1 [1, -1] VM1563:15
undefined
g1(1,2)
G1 1 2 [1, 2] VM1563:22
G2 1 2 [1, 2] VM1563:27
G2 -2 -3 [-2, -3] VM1563:30
G1 1 2 [1, 2] VM1563:24
undefined
h1([1,2],[3,4])
H1 [1, 2] [3, 4]
[Array[2]Array[2]]
VM1563:34
H2 [1, 2] [3, 4]
[Array[2]Array[2]]
VM1563:39
H2 [-2, 2] [-3, 4]
[Array[2]Array[2]]
VM1563:42
H1 [-2, 2] [-3, 4]
[Array[2]Array[2]]

fの結果は予想通りだと思います
普通に書き換えられます

gはapply使っているので変更はg2の関数内だけになります
applyは配列をそれぞれの引数に展開しているので
x.apply(null, y);
x(y[0], y[1], y[2], ....);
となっていてy[0]やy[1]がプリミティブ値なら変更されないということです
applyの第一引数は今回関係ないので略してます
(nullなので.bind(window)するだけです)

hはy[0]にあたる部分をプリミティブ値ではなく配列にして参照になるようにしてます
そのおかげでh2の書き換えがh1でも反映されてます


複雑ですね☆