332

場合によっては、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
}

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

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

4

12 に答える 12

346

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: ...});
于 2012-08-03T13:32:16.777 に答える
85

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

于 2012-08-03T12:54:44.687 に答える
35

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

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

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

そして、それはうまく機能します。ただし、ほとんどのIDEに関しては、多くの開発者がIDE内の型/引数のヒントに依存しています。私は個人的にPHPStormを使用しています(Python用のPyCharmやObjectiveC用のAppCodeなどの他のJetBrainsIDEと一緒に)

そして、「オブジェクトを渡す」トリックを使用する際の最大の問題は、関数を呼び出すときに、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を検索するのにかかる時間と比較すると簡単です。

これで、新しいコードを作成するときにすべてのパラメーターとタイプができました

于 2014-07-10T20:06:24.677 に答える
26

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

someFunction(70, 115);

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

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

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

于 2014-06-03T13:29:59.273 に答える
7

別の方法は、適切なオブジェクトの属性を使用することです。たとえば、次のようになります。

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」のアイデアと組み合わせて、既存の関数から一般的な方法でそのようなオブジェクトを作成することもできます。つまり、パラメーターごとに、関数の部分的に評価されたバージョンに評価できるメンバーが作成されます。

このアイデアはもちろん、Schönfinkeling別名カリー化に関連しています。

于 2013-02-07T15:40:38.117 に答える
3

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

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

基本的にf(...g(o))、spread構文を使用している場所でその構成を呼び出しており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してwidthashおよびwinsideし、関数を短く簡潔にする一方で、わかりやすくするためにフルネームで呼び出すことができます。

// 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})

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

于 2019-08-25T03:11:21.957 に答える
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));
于 2015-05-02T23:43:58.413 に答える
1

NB。コメントで述べられているように、2016年の私の答えは正しくなく誤解を招くものではありません。

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
于 2016-08-18T08:58:06.727 に答える
1

これは確かに擬似コードですが、機能すると思います(Typescriptで機能することはわかっていますが、JavaScriptに採用しています)。

// Target Function
const myFunc = (a=1,b=2,c=3) => {a+b+c}

// Goal usage:
myFunc(a=5, b=6) // 14
myFunc(c=0) // 3

// set your defaults
const myFuncDefaults = {a:1, b:2, c:3};

// override them with passed params
const myFuncParams = (params) => { return Object.assign(myFuncDefaults, params)}


// use the overloaded dict as the input
const myFunc2 = (params) => {
  let {a, b, c} = myFuncParams(params);
  return myFunc(a, b, c)
}

// Usage:
myFunc({a:5, b:6}) // 14
myFunc({c:0}) // 3

// Written more succinctly:
const myFunc = (params) => {
  let {a,b,c} = Object.assign({a:1, b:2, c:3}, params)
  return a + b + c
}

FWIW Typescriptは、この種のヒントを提供します。

interface IParams {
  a: number;
  b: number;
  c: number;
}

const myFunc = (params: Partial<IParams>): number => {
  const default: IParams = {a:1, b:2, c:3};
  let {a, b, c} = Object.assign(default, params)
  return a + b + c
}
于 2021-06-11T06:25:17.140 に答える
0

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

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

于 2019-08-02T01:06:08.453 に答える
0

はい、まあ、ある種。私は2つの解決策を見つけました。1つだけ説明します。

このソリューションでは、位置引数thoを放棄します。

オブジェクト(Pythonのdictとほぼ同じ)を使用して引数を渡すことができます。

この例では、この関数を使用して画像ファイルの名前を生成しています

//first we define our function with just ONE argument
function name_of_img(img_desc){

  // with this step, any undefined value will be assigned a value
  if(img_desc.size == undefined) {img_desc.size = "400x500"}
  if(img_desc.format == undefined) {img_desc.format = ".png"}

  console.log(img_desc.size + img_desc.format)
}

//notice inside our function we're passing a dict/object
    
name_of_img({size: "200x250", format=".jpg"})
// returns "200x250.jpg"

name_of_img({size: "1200x950"})
// returns "1200x950.png"

この例を変更できるので、位置引数も使用できます。また、無効な引数を渡すことができるように変更することもできます。これについてgithubリポジトリを作成すると思います。

于 2021-10-02T09:58:07.257 に答える
-3

一般的に信じられていることとは異なり、名前付きパラメーターは、以下に示すように、単純で適切なコーディング規則を使用して、標準の旧式のJavaScript(ブールパラメーターのみ)に実装できます。

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

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

警告:

  • 引数の順序は保持する必要がありますが、関数のシグネチャをgrepしたり、IDEを使用したりしなくても、どの実際の引数がどの仮パラメータを対象としているのかが明確になるため、パターンは引き続き役立ちます。

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

于 2019-08-05T11:38:20.043 に答える