【JavaScript】 関数内で使用できるargumentsオブジェクトってなんだ?
更新日:2020/02/29
JavaScriptで関数を呼び出すと、関数内部でargumentsというオブジェクトが作成されます。
これって何なのか?
調べてみました。
argumentsオブジェクトとは
調べてみるとargumentsオブジェクトは、引数を格納したオブジェクトでした。
argumentsを確認してみる
function a(){
console.log( arguments );
}
a( 2 , 3 , 4 , 5 );
上のコードを実行すると、コンソールにargumentsの内容が表示されます。
Arguments(4) {
0: 2
1: 3
2: 4
3: 5
length: 4
callee: ƒ a()
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
}
数値プロパティ
キー1から3までに、呼び出し時に指定した4つの引数がセットされています。
argumentsオブジェクトにforEach()は使えません。
for(let i = 0; i < arguments.length ; i ++){ }
や
for( let val of arguments){ }
で列挙しましょう。
lengthプロパティ
lengthは、引数の数です。
calleeプロパティ
calleeは、今実行している関数です。
関数aを次のように変更して、arguments.calleeを呼び出してみます。
arguments.calleeを呼び出してみる
function a(){
console.log( arguments );
if( arguments[0] === 2) arguments.callee( "a" , "b" );
}
最初 if( arguments[0] === 2) を入れてなくて、無限ループしてたいへんなことになりました(汗
arguments.calleeで呼び出した結果、次の内容がコンソールに表示されました。
Arguments {
0: "a"
1: "b"
length: 2
callee: ƒ a()
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
}
関数aが呼び出されていますね。
ただしstrictモードではarguments.calleeを使用できないので、普段からこのメソッドは使用しないほうがよさそうです。
Symbol(Symbol.iterator)プロパティ
引数を列挙できるイテレータを作成するメソッドです。
次のように for-of で使用します。
argumentsオブジェクトのSymbol.iteratorを使用
for(let v of arguments[Symbol.iterator]( ) ){
console.log(v);
}
ただしargumentsオブジェクト自身がイテラブルなオブジェクトなので、次のように記述できます。
argumentsオブジェクトのSymbol.iteratorを使用
for(let v of arguments ){
console.log(v);
}
argumentsオブジェクトはアロー関数では使用できない
アロー関数はargumentsオブジェクトを作成しません。
そのため、次のコードはエラーになります。
アロー関数とargumentsオブジェクト
let a = ( ) =>
console.log(arguments); // ReferenceError: arguments is not defined
a( 1 , 4 );
argumentsオブジェクトは可変長の引数に利用することが多いです。
この場合は、レスト構文で代用可能です。
argumentsオブジェクトのかわりにレスト構文使用
let a = ( ...b ) =>
console.log( b ); // Array [ 1, 4 ]
a( 1 , 4 );
参考:【JavaScript】 アロー関数は何者!?かっこいいだけじゃない!
参考:【JavaScript】 コード中の「...」は意味があった
配列渡しとレスト構文でのargumentsオブジェクト
関数に配列を渡すと、argumentsオブジェクトに一つの要素としてセットされます。
function a( b ){
console.log(arguments); // Arguments{ 0 : Array[ 1 , 4 ], length:1 ・・・
console.log( b ); // Array[ 1 , 4 ]
}
a( [ 1 , 4 ]);
レスト構文で受け取ると、それぞれの値がargumentsオブジェクトにセットされます。
function a( ...b ){
console.log(arguments); // Arguments{ 0 :1, 1 : 4 , length:1 ・・・
console.log( b ); // Array[ 1 , 4 ]
}
a( 1 , 4 );
引数bとして受け取る値は、同じ配列です。
argumentsには、呼び出し時の引数の位置そのままでセットされるようですね。
argumentsで引数の型などをチェックするときは、注意が必要かもしれませんね。
と思ったら...
スプレッド構文で値を受け取ると、それぞれの値がargumentsオブジェクトにセットされます。
function a( b,c ){
console.log(arguments); // Arguments{ 0 :1, 1 : 4 , length:1 ・・・
}
a( ...[1,4] );
arguments[ 0 ] は [ 1 ,4 ] にはならないようです。
難しいですね。
引数とargumentsオブジェクトの関係
引数として得た変数と、argumentsオブジェクトの値は状況によって参照であったり、別々の値だったりします。
非常に煩雑ですので、状況を整理しておきます。
strictは別値
strictモードでは、引数とargumentsオブジェクトの値は各々別のものを参照します。
"use strict";
function a( b ){
arguments[0] = 20;
console.log( b ); // 2
}
a(2);
どちらかの値を変更しても、もう一方に影響しません。
非strictは基本的に同じ値
非strictモードでは、引数とargumentsオブジェクトの値は同じものを参照します。
// "use strict";
function a( b ){
arguments[0] = 20;
console.log( b ); // 20
}
a(2);
一方を変更すると、他方も同じ値になります。
非strictでデフォルト値が含まれると別値
非strictモードでもデフォルト値が指定されると、引数とargumentsオブジェクトの値は別のものを参照します。
function a( a = 10 , b ){
arguments[ 0 ] = 20;
arguments[ 1 ] = 30;
console.log( a ); // Array [ 2 , 3 ]
}
a(2,3);
一方を変更しても、他方に影響しません。
非strictでレスト構文が含まれると別値
非strictモードでもレスト構文(レストパラメータ)が指定されると、引数とargumentsオブジェクトの値は別のものを参照します。
function a( a = 10 , b ){
arguments[ 0 ] = 20;
arguments[ 1 ] = 30;
console.log( a ); // Array [ 2 , 3 ]
}
a(2,3);
こちらもデフォルト値と同様に、一方を変更しても他方に影響しません。
非strictでスプレッド構文は同値
非strictモードでスプレッド構文で引数を受け取ったときは、引数とargumentsオブジェクトの値は同じものを参照します。
function a( b , c ){
arguments[ 0 ] = 20;
arguments[ 1 ] = 30;
console.log( b, c ); // 20 30
}
a( ...[ 2 , 3 ]);
非strictで分割代入が含まれると別値
非strictモードで分割代入とかすると、引数とargumentsオブジェクトの値は別のものを参照します。
function a( [ b ,c ] ){
arguments[ 0 ] = 20;
arguments[ 1 ] = 30;
console.log( b, c ); // 2 3
}
a( [ 2 , 3 ]);
引数で分割代入はあまり使わなそうですが…
こちらもデフォルト値と同様に、一方を変更しても他方に影響しません。
argumentsの使いどころ
関数を定義するとき、引数の名前を考えなくていいのでargumentsオブジェクトは便利ですが...
コードの可読性が落ちるので、引数を指定するべきです。
引数の数が状況によって変わるなら、argumentsオブジェクトを使えそうです。
しかしレスト構文を使用したほうがスッキリすることが多いです。
例えば次のように記述できます。
可変長引数をレスト構文で実現
function a( type , ...param ){
switch( type ){
case 1:
// param[]から3つ参照
break;
case 2:
// param[]から5つ参照
break;
}
}
argumentsオブジェクトを使用する機会は、今のところなさそうです。
まとめ
argumentsオブジェクトはstrictモードかどうかで動作が変わったり、アロー関数で使用できないなど、注意する点が多いようです。
あまり使用しないほうがよさそうですね。
これまでの解説が無駄になったかも…
更新日:2020/02/29
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。