JavaScriptでのオプションパラメータの処理


127

1、2、または3つのパラメーターを取ることができる静的なJavaScript関数があります。

function getData(id, parameters, callback) //parameters (associative array) and callback (function) are optional

特定のパラメーターが未定義かどうかを常にテストできることはわかっていますが、渡されたものがパラメーターとコールバックのどちらであるかはどうすればわかりますか?

これを行う最良の方法は何ですか?


渡される可能性のあるものの例:

1:

getData('offers');

2:

var array = new Array();
array['type']='lalal';
getData('offers',array);

3:

var foo = function (){...}
getData('offers',foo);

4:

getData('offers',array,foo);

2
渡される可能性のあるものの例を示すことができますか?
James Black、

回答:


163

関数に渡された引数の数がわかり、2番目の引数が関数かどうかを確認できます。

function getData (id, parameters, callback) {
  if (arguments.length == 2) { // if only two arguments were supplied
    if (Object.prototype.toString.call(parameters) == "[object Function]") {
      callback = parameters; 
    }
  }
  //...
}

次のように引数オブジェクトを使用することもできます。

function getData (/*id, parameters, callback*/) {
  var id = arguments[0], parameters, callback;

  if (arguments.length == 2) { // only two arguments supplied
    if (Object.prototype.toString.call(arguments[1]) == "[object Function]") {
      callback = arguments[1]; // if is a function, set as 'callback'
    } else {
      parameters = arguments[1]; // if not a function, set as 'parameters'
    }
  } else if (arguments.length == 3) { // three arguments supplied
      parameters = arguments[1];
      callback = arguments[2];
  }
  //...
}

興味がある場合は、John ResigによるJavaScriptでのメソッドのオーバーロードをシミュレートする手法についての記事をご覧ください


なぜObject.prototype.toString.call(parameters)== "[object Function]"を使用し、typeof(parameters)=== 'function'を使用しないのですか?それらの間にいくつかの重要な違いはありますか?PSあなたが言及する記事は後者を使用しているようです
Tomer Cagan

@TomerCagan好みの問題だと思います。この質問の下のトピックについて、いくつかの良い答え/コメントがあります。
Philiiiiiipp 2016

75

Er-これは、正しい順序ではない引数を使用して関数を呼び出していることを意味します...これはお勧めしません。

代わりに次のように関数にオブジェクトをフィードすることをお勧めします。

function getData( props ) {
    props = props || {};
    props.params = props.params || {};
    props.id = props.id || 1;
    props.callback = props.callback || function(){};
    alert( props.callback )
};

getData( {
    id: 3,
    callback: function(){ alert('hi'); }
} );

利点:

  • 引数の順序を考慮する必要はありません
  • 型チェックを行う必要はありません
  • 型チェックが必要ないため、デフォルト値を定義する方が簡単です
  • より少ない頭痛。4番目の引数を追加した場合、型チェックを毎回更新する必要があり、4番目または連続したものも関数である場合はどうなるでしょうか。

欠点:

  • コードをリファクタリングする時間

選択の余地がない場合は、関数を使用して、オブジェクトが実際に関数であるかどうかを検出できます(最後の例を参照)。

注:これは関数を検出する適切な方法です。

function isFunction(obj) {
    return Object.prototype.toString.call(obj) === "[object Function]";
}

isFunction( function(){} )

「これは、ESのバグが原因で、99%の確率で機能します。」詳細を説明できますか?なぜうまくいかないのでしょうか?
jd。

関数を検出するための正しいコードを追加しました。バグはここにあると思います:bugs.ecmascript.org/ticket/251
meder omuraliev 09/10/10

オブジェクトを1つだけフィードすることを強くお勧めします。そうでない場合は、CMSの方法を使用します。
meder omuraliev 2009年

ああ…いまいましい…ちょうど同じアイデアを投稿した。
Arnis Lapsa

1
考えられるもう1つの欠点は、インテリセンスの欠如です。これは大したことではないと思いますが、注意する必要があります。
Edyn


2

受信したパラメータの種類を確認してください。arguments2番目のパラメーターは「パラメーター」と「コールバック」になることがあり、パラメーターに名前を付けると誤解を招く可能性があるため、配列を使用する必要があるかもしれません。


2

私はこれがかなり古い質問であることを知っていますが、最近これに対処しました。このソリューションについてのご意見をお聞かせください。

引数を強く入力してオプションにすることができるユーティリティを作成しました。基本的には、関数をプロキシでラップします。引数をスキップすると、undefinedになります。同じタイプの複数のオプションの引数が隣接していると、風変わりになることがあります。(タイプの代わりに関数を渡してカスタム引数チェックを行うオプションや、各パラメーターのデフォルト値を指定するオプションがあります。)

実装は次のようになります。

function displayOverlay(/*message, timeout, callback*/) {
  return arrangeArgs(arguments, String, Number, Function, 
    function(message, timeout, callback) {
      /* ... your code ... */
    });
};

明確にするために、ここで何が起こっているのですか:

function displayOverlay(/*message, timeout, callback*/) {
  //arrangeArgs is the proxy
  return arrangeArgs(
           //first pass in the original arguments
           arguments, 
           //then pass in the type for each argument
           String,  Number,  Function, 
           //lastly, pass in your function and the proxy will do the rest!
           function(message, timeout, callback) {

             //debug output of each argument to verify it's working
             console.log("message", message, "timeout", timeout, "callback", callback);

             /* ... your code ... */

           }
         );
};

GitHubリポジトリで、arrangeArgsプロキシコードを次の場所に表示できます。

https://github.com/joelvh/Sysmo.js/blob/master/sysmo.js

以下は、リポジトリからコピーされたコメントを含むユーティリティ関数です。

/*
 ****** Overview ******
 * 
 * Strongly type a function's arguments to allow for any arguments to be optional.
 * 
 * Other resources:
 * http://ejohn.org/blog/javascript-method-overloading/
 * 
 ****** Example implementation ******
 * 
 * //all args are optional... will display overlay with default settings
 * var displayOverlay = function() {
 *   return Sysmo.optionalArgs(arguments, 
 *            String, [Number, false, 0], Function, 
 *            function(message, timeout, callback) {
 *              var overlay = new Overlay(message);
 *              overlay.timeout = timeout;
 *              overlay.display({onDisplayed: callback});
 *            });
 * }
 * 
 ****** Example function call ******
 * 
 * //the window.alert() function is the callback, message and timeout are not defined.
 * displayOverlay(alert);
 * 
 * //displays the overlay after 500 miliseconds, then alerts... message is not defined.
 * displayOverlay(500, alert);
 * 
 ****** Setup ******
 * 
 * arguments = the original arguments to the function defined in your javascript API.
 * config = describe the argument type
 *  - Class - specify the type (e.g. String, Number, Function, Array) 
 *  - [Class/function, boolean, default] - pass an array where the first value is a class or a function...
 *                                         The "boolean" indicates if the first value should be treated as a function.
 *                                         The "default" is an optional default value to use instead of undefined.
 * 
 */
arrangeArgs: function (/* arguments, config1 [, config2] , callback */) {
  //config format: [String, false, ''], [Number, false, 0], [Function, false, function(){}]
  //config doesn't need a default value.
  //config can also be classes instead of an array if not required and no default value.

  var configs = Sysmo.makeArray(arguments),
      values = Sysmo.makeArray(configs.shift()),
      callback = configs.pop(),
      args = [],
      done = function() {
        //add the proper number of arguments before adding remaining values
        if (!args.length) {
          args = Array(configs.length);
        }
        //fire callback with args and remaining values concatenated
        return callback.apply(null, args.concat(values));
      };

  //if there are not values to process, just fire callback
  if (!values.length) {
    return done();
  }

  //loop through configs to create more easily readable objects
  for (var i = 0; i < configs.length; i++) {

    var config = configs[i];

    //make sure there's a value
    if (values.length) {

      //type or validator function
      var fn = config[0] || config,
          //if config[1] is true, use fn as validator, 
          //otherwise create a validator from a closure to preserve fn for later use
          validate = (config[1]) ? fn : function(value) {
            return value.constructor === fn;
          };

      //see if arg value matches config
      if (validate(values[0])) {
        args.push(values.shift());
        continue;
      }
    }

    //add a default value if there is no value in the original args
    //or if the type didn't match
    args.push(config[2]);
  }

  return done();
}

2

ArgueJSの使用をお勧めします。

次のように関数を入力するだけです。

function getData(){
  arguments = __({id: String, parameters: [Object], callback: [Function]})

  // and now access your arguments by arguments.id,
  //          arguments.parameters and arguments.callback
}

あなたの例では、idパラメーターを文字列にしたいと考えましたよね?現在、getDataは必須でString id、オプションObject parametersとを受け入れていますFunction callback。投稿したすべてのユースケースは期待どおりに機能します。



1

あなたはこれらのような呼び出しを持つことができると言っています:getData(id、parameters); getData(id、callback)?

この場合、明らかに位置に依存することはできず、タイプの分析に依存する必要があります:getType()そして、必要に応じてgetTypeName()

問題のパラメーターが配列または関数であるかどうかを確認してください。


0

ここでtypeof()を使用したいと思います。

function f(id, parameters, callback) {
  console.log(typeof(parameters)+" "+typeof(callback));
}

f("hi", {"a":"boo"}, f); //prints "object function"
f("hi", f, {"a":"boo"}); //prints "function object"

0

問題が関数のオーバーロードのみの場合(「parameters」パラメーターが「parameters」であり、「callback」ではないかどうかを確認する必要がある場合)、引数の型を気にせず、このアプローチ
使用することをお勧めします。考え方は簡単です。リテラルオブジェクトを使用してパラメーターを組み合わせます。

function getData(id, opt){
    var data = voodooMagic(id, opt.parameters);
    if (opt.callback!=undefined)
      opt.callback.call(data);
    return data;         
}

getData(5, {parameters: "1,2,3", callback: 
    function(){for (i=0;i<=1;i--)alert("FAIL!");}
});

0

これは自明の例かもしれません:

function clickOn(elem /*bubble, cancelable*/) {
    var bubble =     (arguments.length > 1)  ? arguments[1] : true;
    var cancelable = (arguments.length == 3) ? arguments[2] : true;

    var cle = document.createEvent("MouseEvent");
    cle.initEvent("click", bubble, cancelable);
    elem.dispatchEvent(cle);
}

-5

関数をオーバーライドできますか?これは機能しませんか?

function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}

8
いいえ、これは機能しません。エラーは発生しませんが、JavaScriptは常に定義した最新の関数を使用します。
jd。

6
ワオ。クレイジーだと思った。私はそれをテストしました。あなたが正しいです。私の世界は私のために少し変わりました。本番環境でこのようなものがないことを確認するために、今日は多くのJavaScriptを確認する必要があると思います。コメントをありがとう。私はあなたに+1を与えました。
J.Hendrix
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.