web-dev-qa-db-ja.com

JavaScriptの名前付きパラメーター

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
}

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

これを行うライブラリを使用しても大丈夫です。 (または、誰かが私にすでにそうしているものを指し示すことができます)

144
Robin Maben

ES2015以降

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

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 

DEMO

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

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

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

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

または、Function.prototypeを拡張して、次のことができるようにします。

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

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

58
Utkanos

この問題は、しばらくの間私を悩ませてきました。私は経験豊富なプログラマーであり、多くの言語を使用しています。使用する喜びがあったお気に入りの言語の1つはPythonです。 Pythonは、トリックのない名前付きパラメーターをサポートしています。..Python(少し前)を使い始めてから、すべてが簡単になりました。すべての言語が名前付きパラメーターをサポートする必要があると思いますが、そうではありません。

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

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

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

それは素晴らしいことです。ただし、ほとんどのIDEに関しては、開発者の多くはIDE内の型/引数のヒントに依存しています。私は個人的にPHP St​​ormを使用しています(PyCharm for pythonやObjective CのAppCodeなどの他のJetBrains IDEと一緒に)

「オブジェクトを渡す」トリックを使用する際の最大の問題は、関数を呼び出すときに、IDEが単一のタイプヒントを提供することです。型はarg1オブジェクトに入れる必要がありますか?

I have no idea what parameters should go in 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を調べるのにかかる時間と比べると些細なことです。

Now, I have all the parameters and types when creating new code

23
Ray Perea

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

someFunction(70, 115);

なぜ次のことをしないのですか

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

確かに、それはコードの余分な行ですが、読みやすさで勝ちます。

17
dav_i

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

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)

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

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

6
Udo Klein

別の方法があります。オブジェクトを参照渡しする場合、そのオブジェクトのプロパティは関数のローカルスコープに表示されます。私はこれが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));
3
Allen Ellison

Node-6.4.0(process.versions.v8 = '5.0.71.60')およびNode Chakracore-v7.0.0-pre8を試してから、Chrome-52(V8 = 5.2.361.49)を試してみました名前付きパラメーターがalmost実装されていることに気付きましたが、その順序は依然として優先されます。 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
1
jgran

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

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

0
Vince Spicer