JavaScriptの関数呼び出しで名前付きパラメーターを提供する方法はありますか?


207

C#の名前付きパラメーター機能が非常に便利な場合があります。

calculateBMI(70, height: 175);

これをJavaScriptで使用したい場合、何を使用できますか?


私が欲しくないのはこれです:

myFunction({ param1: 70, param2: 175 });

function myFunction(params){
  // Check if params is an object
  // Check if the parameters I need are non-null
  // Blah blah
}

私がすでに使用したそのアプローチ。別の方法はありますか?

これを行うために任意のライブラリを使用しても大丈夫です。


これは可能だとは思わないが、いくつかの未定義のものを空の場所に置くことを試みることができる。どちらが悪いのでしょう。オブジェクトを使用してください。
Vladislav Qulin、2012

14
いいえ、JavaScript / EcmaScriptは名前付きパラメーターをサポートしていません。ごめんなさい。
smilly92

1
私はすでにそれを知っています。ありがとう。私はFunction、JavaScript の既存の機能でできることを調整する方法を探していました。
Robin Maben

1
FunctionJavascript の既存のものは、Javascriptのコア構文を変更できません
Gareth

2
JavaScriptがこの機能をサポートしているとは思いません。名前付きパラメーターに最も近いのは、(1)コメントを追加するcalculateBMI(70, /*height:*/ 175);、(2)オブジェクトを提供するcalculateBMI(70, {height: 175})、または(3)定数を使用することconst height = 175; calculateBMI(70, height);です。
tfmontague 2017

回答:


212

ES2015以降

ES2015では、名前付きパラメーターをシミュレートするためにパラメーター分解を使用できます。呼び出し元がオブジェクトを渡す必要がありますが、デフォルトのパラメーターも使用すると、関数内のすべてのチェックを回避できます。

myFunction({ param1 : 70, param2 : 175});

function myFunction({param1, param2}={}){
  // ...function body...
}

// Or with defaults, 
function myFunc({
  name = 'Default user',
  age = 'N/A'
}={}) {
  // ...function body...
}

ES5

あなたが望むものに近づく方法がありますが、それはの出力に基づいています Function.prototype.toString [ES5]の。これはある程度実装に依存しているため、ブラウザ間の互換性がない場合があります。

オブジェクトのプロパティを対応するパラメーターに関連付けることができるように、関数の文字列表現からパラメーター名を解析するという考え方です。

関数呼び出しは次のようになります

func(a, b, {someArg: ..., someOtherArg: ...});

どこ a、およびbは位置引数であり、最後の引数は名前付き引数を持つオブジェクトです。

例えば:

var parameterfy = (function() {
    var pattern = /function[^(]*\(([^)]*)\)/;

    return function(func) {
        // fails horribly for parameterless functions ;)
        var args = func.toString().match(pattern)[1].split(/,\s*/);

        return function() {
            var named_params = arguments[arguments.length - 1];
            if (typeof named_params === 'object') {
                var params = [].slice.call(arguments, 0, -1);
                if (params.length < args.length) {
                    for (var i = params.length, l = args.length; i < l; i++) {
                        params.push(named_params[args[i]]);
                    }
                    return func.apply(this, params);
                }
            }
            return func.apply(null, arguments);
        };
    };
}());

どちらを使用するか:

var foo = parameterfy(function(a, b, c) {
    console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);     
});

foo(1, 2, 3); // a is 1  | b is 2  | c is 3
foo(1, {b:2, c:3}); // a is 1  | b is 2  | c is 3
foo(1, {c:3}); // a is 1  | b is undefined  | c is 3
foo({a: 1, c:3}); // a is 1  | b is undefined  | c is 3 

デモ

いくつかの欠点がありますこのアプローチにます(警告されています!):

  • 最後の引数がオブジェクトの場合、「名前付き引数オブジェクト」として扱われます
  • 関数で定義した引数と同じ数の引数を常に取得しますが、それらの一部は値を持つ場合がありますundefined(まったく値がない場合とは異なります)。つまり、を使用arguments.lengthして、渡された引数の数をテストすることはできません。

ラッパーを作成する関数を使用する代わりに、引数として関数とさまざまな値を受け入れる関数を使用することもできます。

call(func, a, b, {posArg: ... });

Function.prototypeあなたができるように拡張することもできます:

foo.execute(a, b, {posArg: ...});

ええと...これはその例です:jsfiddle.net/9U328/1(ただし、使用Object.definePropertyしてに設定enumerableする必要がありますfalse)。ネイティブオブジェクトを拡張するときは常に注意が必要です。全体のアプローチは少しハックに感じられるので、私はそれが今そして永遠に機能することを期待しません;)
Felix Kling

了解しました。これを使ってみることにしましょう。回答としてマークします!...今のところ;)
Robin Maben

1
非常にマイナーなnitpick:このアプローチでEcmaScript 6の矢印関数をキャッチできるとは思いません 。現在は大きな懸念事項ではありませんが、回答の警告セクションで言及する価値があるかもしれません。
NobodyMan、2015年

3
@NobodyMan:はい。アロー関数が物になる前に私はこの答えを書きました。ES6では、実際にはパラメーターのデストラチャに頼っていました。
Felix Kling 2015年

1
undefinedvs「値なし」の問題については、これがJS関数のデフォルト値の動作方法でありundefined、欠損値として扱われることを追加できます。
Dmitri Zaitsev

71

いいえ -オブジェクトアプローチはこれに対するJavaScriptの答えです。関数が個別のパラメータではなくオブジェクトを想定している場合、これには問題はありません。


35
@RobertMaben-尋ねられた特定の質問に対する答えは、宣言された変数または関数を特定の名前空間に存在することを知らずに収集するネイティブな方法はないということです。答えが短いからといって、答えとしての適合性を否定するものではありません。同意していただけませんか。「いいえ、不可能」の線に沿って、はるかに短い答えがあります。短いかもしれませんが、質問への答えでもあります。
Mitya

3
最近はes6もあります:2ality.com/2011/11/keyword-parameters.html
slacktracer

1
これは間違いなく公正な答えです。「名前付きパラメータ」は言語機能です。オブジェクトアプローチは、そのような機能がない場合の次善の策です。
fatuhoku 2016年

32

この問題は、長い間私の頭を悩ませてきました。私は、多くの言語を身につけている熟練したプログラマーです。私が使用して喜んでいる私のお気に入りの言語の1つはPythonです。Pythonはトリックなしで名前付きパラメーターをサポートします。...(少し前に)Pythonを使い始めて以来、すべてが簡単になりました。すべての言語が名前付きパラメーターをサポートする必要があると私は信じていますが、それは事実ではありません。

多くの人は、「オブジェクトを渡す」トリックを使用して、パラメータに名前を付けていると言います。

/**
 * My Function
 *
 * @param {Object} arg1 Named arguments
 */
function myFunc(arg1) { }

myFunc({ param1 : 70, param2 : 175});

そして、それはうまくいきますが、それ以外のほとんどのIDEに関しては、多くの開発者がIDE内の型/引数のヒントに依存しています。私は個人的にPHPストームを使用しています(PythonのPyCharmやObjective CのAppCodeなどの他のJetBrains IDEと一緒に)

そして、「オブジェクトを渡す」トリックを使用する上での最大の問題は、関数を呼び出すときに、IDEが単一の型ヒントを提供することです。それだけです...どのパラメーターと型がarg1オブジェクト?

arg1にどのパラメーターを入れればよいかわかりません。

だから... "オブジェクトを渡す"トリックは私にはうまくいきません...実際に、関数が期待するパラメーターを知る前に、各関数のdocblockを調べる必要があると、さらに頭痛の種になります。...確かに、既存のコードを維持しているが、新しいコードを書くのは恐ろしいことです。

さて、これは私が使用する手法です。...今、それにはいくつかの問題があるかもしれません、そして一部の開発者は私が間違っていると私に言うかもしれません、そして私はこれらのことになると心を開いています...私は常にタスクを達成するためのより良い方法を検討したいと思っています...したがって、この手法に問題がある場合は、コメントを歓迎します。

/**
 * My Function
 *
 * @param {string} arg1 Argument 1
 * @param {string} arg2 Argument 2
 */
function myFunc(arg1, arg2) { }

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');

このように、私は両方の長所を持っています... IDEが適切な引数のヒントをすべて提供するので、新しいコードは簡単に書くことができます...そして、後でコードを維持しながら、一目でわかるだけでなく、関数に渡される値だけでなく、引数の名前も。私が目にする唯一のオーバーヘッドは、グローバル名前空間を汚染しないようにするために、引数名をローカル変数として宣言することです。確かに、少し余分な入力が必要ですが、新しいコードを記述したり、既存のコードを維持したりするときにdocblockを検索するのにかかる時間と比べると、取るに足りません。

今、私は新しいコードを作成するときにすべてのパラメーターと型を持っています


2
この手法の唯一の点は、パラメーターの順序を変更できないことです...私は個人的には問題ありません。
レイペリア2014

13
これは、将来のメンテナが来て、引数の順序を変更できると思ったとき(しかし、明らかにできない)に問題を求めているようです。
誰もいない

1
@AndrewMedico同意します... Pythonのように引数の順序を変更できるように見えます。私がそれについて言えることは、引数の順序を変更するとプログラムが壊れたときにすぐにわかるということだけです。
レイペリア2014

7
私は主張したいmyFunc(/*arg1*/ 'Param1', /*arg2*/ 'Param2');よりも良好であるmyFunc(arg1='Param1', arg2='Param2');ことが実際の名前付き引数が起こっているよりも、読者をだましのチャンスを持っていないので、
エリック・

2
提案されたパターンは名前付き引数とは何の関係もありません。順序は依然として重要です。名前は実際のパラメーターと常に同期している必要はありません。名前空間は不要な変数で汚染されており、関係がないことを意味しています。
Dmitri Zaitsev

25

単に呼び出すのではなく、各パラメータを明確にしたい場合

someFunction(70, 115);

次のことをしてみませんか

var width = 70, height = 115;
someFunction(width, height);

確かに、これは追加のコード行ですが、読みやすさを優先しています。


5
+1はKISSの原則に従うため、さらにデバッグにも役立ちます。ただし、わずかなパフォーマンスヒットはありますが(http://stackoverflow.com/questions/9672635/javascript-var-statement-and-performance)、各変数は独自の行にあるはずです。
clairestreb 2014年

21
コードの追加行だけでなく、引数の順序とオプションにすることも重要です。したがって、名前付きパラメーターを使用してこれを書くことができます。someFunction(height: 115);ただし、someFunction(height);実際に幅を設定する場合は、
フランシスコプレセンシア2015

その場合、CoffeeScriptは名前付き引数をサポートします。それだけで書くことができますsomeFunction(width = 70, height = 115);。変数は、生成されるJavaScriptコードの現在のスコープの先頭で宣言されます。
Audun Olsen

6

別の方法は、適切なオブジェクトの属性を使用することです。

function plus(a,b) { return a+b; };

Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}}, 
         b: function(y) { return { a: function(x) { return plus(x,y) }}}};

sum = Plus.a(3).b(5);

もちろん、この構成された例では、少し意味がありません。しかし、関数が次のように見える場合

do_something(some_connection_handle, some_context_parameter, some_value)

もっと便利かもしれません。また、 "parameterfy"アイデアと組み合わせて、既存の関数から一般的な方法でそのようなオブジェクトを作成することもできます。つまり、パラメーターごとに、関数の部分評価バージョンに評価できるメンバーを作成します。

このアイデアはもちろんシェーンフィンケリング、別名カリーに関連しています。


これはすばらしいアイデアであり、これを引数のイントロスペクショントリックと組み合わせるとさらに良くなります。残念ながら、それはオプションの引数での作業には不十分です
Eric

2

別の方法があります。参照によってオブジェクトを渡す場合、そのオブジェクトのプロパティは関数のローカルスコープに表示されます。これはSafari(他のブラウザーをチェックしていない)で機能し、この機能に名前が付いているかどうかはわかりませんが、以下の例はその使用方法を示しています。

実際には、これがすでに使用しているテクニックを超える機能的価値を提供するとは思いませんが、意味的には少しクリーンです。また、オブジェクト参照またはオブジェクトリテラルを渡す必要があります。

function sum({ a:a, b:b}) {
    console.log(a+'+'+b);
    if(a==undefined) a=0;
    if(b==undefined) b=0;
    return (a+b);
}

// will work (returns 9 and 3 respectively)
console.log(sum({a:4,b:5}));
console.log(sum({a:3}));

// will not work (returns 0)
console.log(sum(4,5));
console.log(sum(4));

2

Node-6.4.0(process.versions.v8 = '5.0.71.60')とNode Chakracore-v7.0.0-pre8を試してからChrome-52(V8 = 5.2.361.49)を試してみると、名前付きパラメーターがほぼ実装されていますが、その順序はまだ優先されます。ECMA標準の内容がわかりません。

>function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) }

> f()
a=1 + b=2 = 3
> f(a=5)
a=5 + b=2 = 7
> f(a=7, b=10)
a=7 + b=10 = 17

しかし、注文が必要です!! それは標準的な行動ですか?

> f(b=10)
a=10 + b=2 = 12

10
それはあなたが考えていることをしていません。式の結果b=1010であり、それが関数に渡されます。f(bla=10)これも機能します(変数に10を割り当てbla、後で値を関数に渡します)。
Matthias Kestenholz 2016年

1
これは、副作用としてグローバルスコープで変数を作成することでもaありbます。
Gershom 2018

2

fオブジェクトとして渡された名前付きパラメーターで関数を呼び出す

o = {height: 1, width: 5, ...}

基本的にf(...g(o))、私がスプレッド構文を使用している場所でその構成を呼び出し、g、オブジェクトの値とパラメーターの位置を関連付ける「バインディング」マップです。

バインディングマップは、欠けている成分であり、キーの配列で表すことができます。

// map 'height' to the first and 'width' to the second param
binding = ['height', 'width']

// take binding and arg object and return aray of args
withNamed = (bnd, o) => bnd.map(param => o[param])

// call f with named args via binding
f(...withNamed(binding, {hight: 1, width: 5}))

3つの分離された要素に注意してください関数、名前付き引数を持つオブジェクト、バインディングです。この分離により、この構成を柔軟に使用できるようになります。バインディングは、関数の定義で任意にカスタマイズし、関数呼び出し時に任意に拡張できます。

たとえば、あなたは、省略することもできますheightし、widthなどhwあなたはまだ明確にするための完全な名前とそれを呼び出すようにしたい一方で、あなたの関数の定義の中、それは短く、きれいにするために:

// use short params
f = (h, w) => ...

// modify f to be called with named args
ff = o => f(...withNamed(['height', 'width'], o))

// now call with real more descriptive names
ff({height: 1, width: 5})

この柔軟性は、元のパラメーター名が失われて関数を任意に変換できる関数型プログラミングにも役立ちます。


0

これはPythonから来て、私を悩ませました。位置オブジェクトとキーワードオブジェクトの両方を受け入れる単純なラッパー/プロキシノードを作成しました。

https://github.com/vinces1979/node-def/blob/master/README.md


私が正しく理解している場合、ソリューションでは、関数の定義で位置パラメータと名前付きパラメータを区別する必要があります。つまり、呼び出し時にこの選択を行う自由がありません。
Dmitri Zaitsev

@DmitriZaitsev:Pythonコミュニティ(キーワード引数は日常的な機能です)では、ユーザーがキーワードでオプションの引数を指定できるようにすることは非常に良いアイデアであると考えています。これはエラーを回避するのに役立ちます。
Tobias

JSにそうするユーザーを強制@Tobiasは解決される問題である:f(x, opt)ここで、optオブジェクトです。エラーを回避するのに役立つのか、それともエラーを作成するのに役立つのか(キーワードのスペルミスやキーワード名を覚えるのが原因の痛みなど)、問題は残ります。
Dmitri Zaitsev

@DmitriZaitsev:(もちろん)キーワード引数はコア言語の機能なので、Pythonではエラーを厳密に回避します。キーワードのみの引数の場合、Python 3には特別な構文があります(Python 2では、kwargs dictからキーを1つずつポップし、TypeError不明なキーが残っている場合は最後にaを発生させます)。あなたのf(x, opt)ソリューションでf関数がPython 2のようなものを実行できるようにしますが、すべてのデフォルト値を自分で処理する必要があります。
トビアス

@Tobiasこの提案はJSに関連していますか?どのように説明しているようですf(x, opt)例えば、あなたが両方をしたいところ、私は、これは質問に答えるためにどのように役立つかを見ていないが、すでに働いているrequest(url)と、request({url: url})それが行うことで可能ではないであろうurlキーワード専用パラメータ。
Dmitri Zaitsev

-1

一般に信じられていることとは逆に、名前付きパラメーターは、以下に示すように、シンプルできちんとしたコーディング規則を使用して、標準の古いJavaScript(ブールパラメーターのみ)で実装できます。

function f(p1=true, p2=false) {
    ...
}

f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)

警告:

  • 引数の順序は維持する必要がありますが、パターンは有用です。これにより、関数のシグネチャをgrepしたり、IDEを使用したりしなくても、どの実引数がどの仮パラメーターを意味するかが明らかになります。

  • これはブール値でのみ機能します。ただし、JavaScriptの一意の型強制セマンティクスを使用して、他の型についても同様のパターンを開発できると確信しています。


ここではまだ名前ではなく、位置で参照しています。
Dmitri Zaitsev

@DmitriZaitsevはい、私は上でもそう言った。ただし、名前付き引数の目的は、各引数の意味を読者に明確にすることです。ドキュメントの一種です。私のソリューションでは、乱雑に見えるコメントに頼らずに、関数呼び出し内にドキュメントを埋め込むことができます。
user234461

これは別の問題を解決します。問題はheight、paramの順序に関係なく名前で渡すことでした。
Dmitri Zaitsev

@DmitriZaitsev実際には、質問はパラメーターの順序について何も言わなかった、またはなぜOPが名前付きパラメーターを使用したかったのか。
user234461

これは、C#を参照することによって行われます。ここでは、paramsを任意の順序で名前で渡すことが重要です。
Dmitri Zaitsev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.