web-dev-qa-db-ja.com

関数へのJavaScript可変数の引数

JavaScriptの関数に "無制限"の変数を許可する方法はありますか?

例:

load(var1, var2, var3, var4, var5, etc...)
load(var1)
483
Timo

もちろん、argumentsオブジェクトを使うだけです。

function foo() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}
755
roufamatic

(ほとんどの)最近のブラウザでは、次の構文で可変数の引数を受け取ることができます。

function my_log(...args) {
     // args is an Array
     console.log(args);
     // You can pass this array as parameters to another function
     console.log(...args);
}

これはちょっとした例です:

function foo(x, ...args) {
  console.log(x, args, ...args, arguments);
}

foo('a', 'b', 'c', z='d')

=>

a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
​    0: "a"
    ​1: "b"
    ​2: "c"
    ​3: "d"
    ​length: 4

ここにドキュメントとその他の例があります。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

119
Ramast

もう1つの選択肢は、コンテキストオブジェクトに引数を渡すことです。

function load(context)
{
    // do whatever with context.name, context.address, etc
}

そしてこのように使う

load({name:'Ken',address:'secret',unused:true})

これは、あなたが望むだけ多くの名前付き引数を追加することができ、関数が適切であると思う限りそれらを使用できる(あるいは使用しない)という利点があります。

109
Ken

私はケンの答えに最もダイナミックであると同意します、そしてそれをさらに一歩進めたいです。もしそれがあなたが異なる引数で複数回呼び出す関数であれば - 私はKenの設計を使うが、それからデフォルト値を加える:

function load(context) {

    var defaults = {
        parameter1: defaultValue1,
        parameter2: defaultValue2,
        ...
    };

    var context = extend(defaults, context);

    // do stuff
}

このように、たくさんのパラメータを持っていても、必ずしも関数を呼び出すたびにそれらを設定する必要がない場合は、デフォルト以外の値を指定することができます。 extendメソッドには、jQueryのextendメソッド($.extend())を使うか、独自のものを作るか、次のようにします。

function extend() {
    for (var i = 1; i < arguments.length; i++)
        for (var key in arguments[i])
            if (arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

これはコンテキストオブジェクトをデフォルト値とマージし、オブジェクト内の未定義の値をデフォルト値で埋めます。

45
mbeasley

はい、ちょうどこのように:

function load()
{
  var var0 = arguments[0];
  var var1 = arguments[1];
}

load(1,2);
18
Canavar

Ramastが指摘したように、restパラメータの構文を使用することをお勧めします。

function (a, b, ...args) {}

... args引数のNiceプロパティをいくつか追加したいだけです。

  1. これは配列であり、引数のようなオブジェクトではありません。これにより、マップやソートなどの機能を直接適用できます。
  2. すべてのパラメータが含まれるのではなく、渡されたパラメータだけが含まれます。例えば。この場合のfunction(a、b、... args)は、arguments.lengthに対する引数3を含みます。
13
Nicola Pedretti

すでに述べたように、argumentsオブジェクトを使用して、可変数の関数パラメーターを取得できます。

同じ引数を使って別の関数を呼び出したい場合は、applyを使用してください。 argumentsを配列に変換することで、引数を追加または削除することもできます。たとえば、この関数はコンソールにログインする前にテキストを挿入します。

log() {
    let args = Array.prototype.slice.call(arguments);
    args = ['MyObjectName', this.id_].concat(args);
    console.log.apply(console, args);
}
12
Peter Tseng

私は一般的に、名前付き引数アプローチが便利で柔軟であることに同意しますが(順序を気にしない限り、引数が最も簡単です)、私はmbeasleyアプローチのコストについて懸念を持っています(デフォルトと拡張を使う)。これは、デフォルト値を引き出すためにかかる極端なコストです。まず、デフォルトは関数内で定義されているため、呼び出しごとにデフォルトになります。 2つ目は、||を使用して名前付きの値を簡単に読み取ってデフォルトを設定することです。この情報を取得するためにさらに別の新しいオブジェクトを作成してマージする必要はありません。

function load(context) {
   var parameter1 = context.parameter1 || defaultValue1,
       parameter2 = context.parameter2 || defaultValue2;

   // do stuff
}

これはほぼ同じ量のコードです(多少多少ありますが)が、実行時コストのごく一部です。

8
mcurland

@roufamaticはargumentsキーワードの使用を示し、@Kenは使用するオブジェクトの優れた例を示しましたが、この例で何が起こっているのかを真に説明していないため、将来の読者を混乱させたり、/methodは可変量の引数/パラメータを取ることを意図しています。

function varyArg () {
    return arguments[0] + arguments[1];
}

他の開発者があなたのコードを見ているとき、この関数がパラメータをとらないと仮定するのはとても簡単です。特に、その開発者が arguments キーワードをよく知っていないのであれば。このため、スタイルガイドラインに従って一貫性を保つことをお勧めします。私はすべての例にグーグルを使用します。

同じ関数に可変パラメータがあることを明示的に説明しましょう。

function varyArg (var_args) {
    return arguments[0] + arguments[1];
}

オブジェクトパラメータVS var_args

オブジェクトがデータマップの唯一の承認され考慮されたベストプラクティスの方法であるため、オブジェクトが必要になる場合があります。連想配列は眉をひそめてお勧めできません。

SIDENOTE: argumentsキーワードは、実際には数字としてキーを使用してオブジェクトを返します。プロトタイプの継承もオブジェクトファミリーです。 JSでの正しい配列使用法については、回答の終わりを参照してください。

この場合、これも明示的に述べることができます。注:この命名規則はGoogleによって提供されているのではなく、パラメーターの型の明示的な宣言の例です。コード内でより厳密な型付きパターンを作成しようとしている場合、これは重要です。

function varyArg (args_obj) {
    return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});

どれを選ぶ?

これはあなたの関数とプログラムの必要性によります。たとえば、渡されたすべての引数にわたって反復プロセスに基づいて値ベースを返すことだけを検討している場合は、 arguments キーワードを使用します。引数の定義とデータのマッピングが必要な場合は、オブジェクトメソッドを使用してください。 2つの例を見てみましょう。

引数の使い方

function sumOfAll (var_args) {
    return arguments.reduce(function(a, b) {
        return a + b;
    }, 0);
}
sumOfAll(1,2,3); // returns 6

オブジェクトの使い方

function myObjArgs(args_obj) {
    // MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
    if (typeof args_obj !== "object") {
        return "Arguments passed must be in object form!";
    }

    return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old

オブジェクトではなく配列にアクセスする( "... args" restパラメータ)

答えの冒頭で述べたように、 arguments キーワードは実際にはオブジェクトを返します。このため、配列に使用したい任意のメソッドを呼び出す必要があります。この例:

Array.prototype.map.call(arguments, function (val, idx, arr) {});

これを回避するには、restパラメータを使用してください。

function varyArgArr (...var_args) {
    return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
6
Brian Ellis

渡されたすべての引数にアクセスするには、関数内でargumentsオブジェクトを使用します。

5
nickytonline

Kenが提案したように名前付きプロパティを持つオブジェクトを渡すと、すべての呼び出しに一時オブジェクトを割り当てて解放するコストが増加することに注意してください。通常の引数を値または参照で渡すのが一般的に最も効率的です。パフォーマンスはそれほど重要ではありませんが、多くのアプリケーションにとっては重要です。

3
phbcanada