web-dev-qa-db-ja.com

JavaScriptでオプションの関数パラメータをするもっと良い方法はありますか?

私はいつもJavaScriptでオプションのパラメータをこのように扱ってきました:

function myFunc(requiredArg, optionalArg){
  optionalArg = optionalArg || 'defaultValue';

  // Do stuff
}

もっと良い方法はありますか?

このような||を使用すると失敗することがありますか?

788
Mark Biek

OptionalArgが渡されるとロジックは失敗しますが、falseと評価されます - 代わりにこれを試してください

if (typeof optionalArg === 'undefined') { optionalArg = 'default'; }

あるいは別のイディオム:

optionalArg = (typeof optionalArg === 'undefined') ? 'default' : optionalArg;

どちらの慣用句を使用しても、意図が最も適切に伝達されます。

1041
Paul Dixon

ECMAScript 2015(別名 "ES6")では、関数宣言でデフォルトの引数値を宣言できます。

function myFunc(requiredArg, optionalArg = 'defaultValue') {
    // do stuff
}

これらについての詳細は MDNに関するこの記事 (記事のタイトルにもかかわらず、JavaScriptでは「引数」とは呼ばれていません)。

これは現在 Firefoxでのみサポートされています しかし、標準が完成したので、サポートが急速に改善することを期待しています。

130
Lachlan Hunt

これが最も簡単で読みやすい方法だと思います。

if (typeof myVariable === 'undefined') { myVariable = 'default'; }
//use myVariable here

Paul Dixonの答え(私の謙虚な意見では)はこれより読みにくいですが、それは好みに帰着します。

insinの答えはもっと進んでいますが、大きな関数にはもっと便利です!

EDIT 11/17/2013 9:33 pm: 私は Parametric と呼ばれる関数(メソッド)の "オーバーロード"を容易にするNode.js用のパッケージを作成しました。

125
trusktr

あなたがリテラルNULLを固定する必要があるなら、あなたはいくつかの問題を抱えているかもしれません。それとは別に、いいえ、あなたはおそらく正しい軌道に乗っていると思います。

他の方法を選ぶ人は、引数リストを繰り返し処理する変数の連想配列を使います。それは少しきれいに見えますが、私はそれが少し(非常に少し)もう少しプロセス/メモリ集約的だと思います。

function myFunction (argArray) {
    var defaults = {
        'arg1'  :   "value 1",
        'arg2'  :   "value 2",
        'arg3'  :   "value 3",
        'arg4'  :   "value 4"
    }

    for(var i in defaults) 
        if(typeof argArray[i] == "undefined") 
               argArray[i] = defaults[i];

    // ...
}
44
Oli

理想的には、オブジェクトを渡してデフォルトオブジェクトで merge それをリファクタリングするので、引数が渡される順序は重要ではありません(下記のこの回答の2番目のセクションを参照)。

しかし、あなたがただ速く、信頼性があり、使いやすくて嵩張らない何かがほしいと思うならば、これを試してください:


デフォルトの引数をいくつでもきれいに素早く修正

  • それは優雅に拡大縮小します:それぞれの新しいデフォルトのための最小の追加コード
  • どこにでも貼り付けることができます。必要な引数と変数の数を変更するだけです。
  • デフォルト値を使ってundefinedを引数に渡したい場合、このように変数はundefinedとして設定されます。このページの他のほとんどのオプションは、undefinedをデフォルト値に置き換えます。

これは、3つのオプション引数(2つの必須引数を含む)にデフォルトを指定する例です。

function myFunc( requiredA, requiredB,  optionalA, optionalB, optionalC ) {

  switch (arguments.length - 2) { // 2 is the number of required arguments
    case 0:  optionalA = 'Some default';
    case 1:  optionalB = 'Another default';
    case 2:  optionalC = 'Some other default';
    // no breaks between cases: each case implies the next cases are also needed
  }

}

簡単なデモ 。これは roenving's answer に似ていますが、デフォルトの引数をいくつでも簡単に拡張でき、更新が簡単で、 Function.argumentsではなくargumentsを使用 です。


より柔軟なオブジェクトの受け渡しとマージ

上記のコードは、デフォルトの引数を設定する多くの方法と同様に、引数を順番外に渡すことはできません。たとえば、optionalCを渡してもoptionalBはデフォルトに戻ります。

そのための良い方法は、オブジェクトを渡してデフォルトのオブジェクトとマージすることです。これは保守性にも役立ちます(コードを読みやすくするように注意してください。今後の共同作業者は、渡されたオブジェクトの可能性のある内容について推測したままにしないでください)。

JQueryを使用した例jQueryを使用しない場合は、代わりにUnderscoreの_.defaults(object, defaults)または これらのオプションを参照 を使用できます。

function myFunc( args ) {
  var defaults = {
    optionalA: 'Some default',
    optionalB: 'Another default',
    optionalC: 'Some other default'
  };
  args = $.extend({}, defaults, args);
}

これが 実際の動作の簡単な例 です。

32
user568458

あなたはそのためにいくつかの異なる方式を使うことができます。私はいつもarguments.lengthについてテストしました:

function myFunc(requiredArg, optionalArg){
  optionalArg = myFunc.arguments.length<2 ? 'defaultValue' : optionalArg;

  ...

- そうしても、おそらく失敗することはありませんが、あなたの方法が失敗する可能性があるかどうかはわかりません。今は、実際に失敗するシナリオを考えることはできません...

そしてパウロは失敗したシナリオを1つ提供しました! - )

17
roenving

Oliの答えと同様に、私は引数Objectとデフォルト値を定義するObjectを使います。少し砂糖を入れて...

/**
 * Updates an object's properties with other objects' properties. All
 * additional non-falsy arguments will have their properties copied to the
 * destination object, in the order given.
 */
function extend(dest) {
  for (var i = 1, l = arguments.length; i < l; i++) {
    var src = arguments[i]
    if (!src) {
      continue
    }
    for (var property in src) {
      if (src.hasOwnProperty(property)) {
        dest[property] = src[property]
      }
    }
  }
  return dest
}

/**
 * Inherit another function's prototype without invoking the function.
 */
function inherits(child, parent) {
  var F = function() {}
  F.prototype = parent.prototype
  child.prototype = new F()
  child.prototype.constructor = child
  return child
}

...これは少し良くすることができます。

function Field(kwargs) {
  kwargs = extend({
    required: true, widget: null, label: null, initial: null,
    helpText: null, errorMessages: null
  }, kwargs)
  this.required = kwargs.required
  this.label = kwargs.label
  this.initial = kwargs.initial
  // ...and so on...
}

function CharField(kwargs) {
  kwargs = extend({
    maxLength: null, minLength: null
  }, kwargs)
  this.maxLength = kwargs.maxLength
  this.minLength = kwargs.minLength
  Field.call(this, kwargs)
}
inherits(CharField, Field)

この方法のいいところは何ですか?

  • あなたは好きなだけ引数を省略することができます - もしあなたが1つの引数の値を上書きしたいだけなら、明示的にundefinedを渡す必要はなく、単にその引数を与えることができます。最後に、あなたが提案した他の方法のいくつかと関係があるように。
  • 他のオブジェクトから継承するオブジェクトに対してコンストラクタ関数を使用する場合、コンストラクタシグネチャでそれらの引数に名前を付ける必要がないので、継承元のオブジェクトのコンストラクタに必要なすべての引数を受け入れるのは簡単です。あるいは、あなた自身のデフォルト値を提供することもできます(上のように、CharFieldFieldのコンストラクタを呼び出すときに、親Objectのコンストラクタがあなたに代わってそれを行わせます)。
  • 継承階層の子オブジェクトは、適切に見えるように親コンストラクタの引数をカスタマイズしたり、独自のデフォルト値を適用したり、特定の値が always になるようにすることができます。
13
Jonny Buchanan

緩い型チェック

簡単に記述できますが、0''falsenull、およびundefinedはデフォルト値に変換されます。これは予期された結果ではない場合があります。

function myFunc(requiredArg, optionalArg) {
    optionalArg = optionalArg || 'defaultValue';
}

厳密な型チェック

長くなりますが、ほとんどの場合をカバーしています。デフォルト値が誤って割り当てられているのは、パラメータとしてundefinedを渡した場合だけです。

function myFunc(requiredArg, optionalArg) {
    optionalArg = typeof optionalArg !== 'undefined' ? optionalArg : 'defaultValue';
}

引数をチェックする変数

すべてのケースをキャッチしますが、書くのが最も不器用です。

function myFunc(requiredArg, optionalArg1, optionalArg2) {
    optionalArg1 = arguments.length > 1 ? optionalArg1 : 'defaultValue';
    optionalArg2 = arguments.length > 2 ? optionalArg2 : 'defaultValue';
}

ES6

残念ながら、現時点ではブラウザのサポートは非​​常に貧弱です。

function myFunc(requiredArg, optionalArg = 'defaultValue') {

}
8
Petr Hurtak

デフォルトを広く使用しているのであれば、これははるかに読みやすくなります。

function usageExemple(a,b,c,d){
    //defaults
    a=defaultValue(a,1);
    b=defaultValue(b,2);
    c=defaultValue(c,4);
    d=defaultValue(d,8);

    var x = a+b+c+d;
    return x;
}

この関数をグローバルスコープで宣言するだけです。

function defaultValue(variable,defaultValue){
    return(typeof variable!=='undefined')?(variable):(defaultValue);
}

使用パターンfruit = defaultValue(fruit,'Apple');

* PSあなたはdefaultValue関数を短い名前に改名することができます、ちょうどdefaultを使わないでください、それはジャバスクリプトの予約語です。

8
Vitim.us

ES2015/ES6では、$.extend()または_.defaults()に代わるObject.assignを利用できます。

function myFunc(requiredArg, options = {}) {
  const defaults = {
    message: 'Hello',
    color: 'red',
    importance: 1
  };

  const settings = Object.assign({}, defaults, options);

  // do stuff
}

このようにデフォルトの引数を使うこともできます。

function myFunc(requiredArg, { message: 'Hello', color: 'red', importance: 1 } = {}) {
  // do stuff
}
5
Yann Bertrand

私は、オプションの変数を扱う上でいくつかの基本的なバリエーションを見てきました。時々、リラックスしたバージョンは便利です。

function foo(a, b, c) {
  a = a || "default";   // Matches 0, "", null, undefined, NaN, false.
  a || (a = "default"); // Matches 0, "", null, undefined, NaN, false.

  if (b == null) { b = "default"; } // Matches null, undefined.

  if (typeof c === "undefined") { c = "default"; } // Matches undefined.
}

変数aで使われている誤ったデフォルトは、例えば Backbone.js で広く使われています。

4
Matt Montag

もしあなたが Underscore ライブラリを使っているなら(あなたはすべきです、それは素晴らしいライブラリです):

_.defaults(optionalArg, 'defaultValue');
3
roo2

EcmaScript 2015のデフォルトのパラメータ を検索して、この質問に着手しました。

ES6 を使用すると、 デフォルトパラメータを設定できます

function doSomething(optionalParam = "defaultValue"){
    console.log(optionalParam);//not required to check for falsy values
}

doSomething(); //"defaultValue"
doSomething("myvalue"); //"myvalue"
2

@ Paulの返信が支持されていない理由はわかりませんが、nullに対する検証が適切な選択です。もっと肯定的な例がもっと理にかなっているでしょう。

JavaScriptでは、見逃したパラメータは、初期化されていない宣言変数のようなものです(単にvar a1;)。そして、等値演算子はundefinedをnullに変換するので、これは値型とオブジェクトの両方でうまく機能します、そしてこれはCoffeeScriptが任意のパラメータを扱う方法です。

function overLoad(p1){
    alert(p1 == null); // Caution, don't use the strict comparison: === won't work.
    alert(typeof p1 === 'undefined');
}

overLoad(); // true, true
overLoad(undefined); // true, true. Yes, undefined is treated as null for equality operator.
overLoad(10); // false, false


function overLoad(p1){
    if (p1 == null) p1 = 'default value goes here...';
    //...
}

しかし、最良のセマンティクスはtypeof variable === 'undefined'の方がわずかに優れているという懸念があります。関数がどのように実装されているかは基礎となるAPIの問題であるため、私はこれを防御しようとはしていません。 APIユーザーには関係ありません。

in演算子を使用して引数が欠落していることを物理的に確認する唯一の方法もここに追加する必要があります。残念ながらパラメータ名では機能しないので、argumentsのインデックスを渡す必要があります。

function foo(a, b) {
    // Both a and b will evaluate to undefined when used in an expression
    alert(a); // undefined
    alert(b); // undefined

    alert("0" in arguments); // true
    alert("1" in arguments); // false
}

foo (undefined);
2

私はここで言及されているいくつかのオプションを試してみましたそしてパフォーマンスはそれらをテストしました。現時点では、logicalorが最も速いようです。これは時間の経過とともに変化する可能性がありますが(JavaScriptエンジンのバージョンは異なります)。

これらは私の結果です(Microsoft Edge 20.10240.16384.0)。

Function executed            Operations/sec     Statistics
TypeofFunction('test');          92,169,505     ±1.55%   9% slower
SwitchFuntion('test');            2,904,685     ±2.91%  97% slower
ObjectFunction({param1: 'test'});   924,753     ±1.71%  99% slower
LogicalOrFunction('test');      101,205,173     ±0.92%     fastest
TypeofFunction2('test');         35,636,836     ±0.59%  65% slower

このパフォーマンステストは簡単に再現できます。 http://jsperf.com/optional-parameters-typeof-vs-switch/2

これはテストのコードです:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
    Benchmark.prototype.setup = function() {
        function TypeofFunction(param1, optParam1, optParam2, optParam3) {
            optParam1 = (typeof optParam1 === "undefined") ? "Some default" : optParam1;
            optParam2 = (typeof optParam2 === "undefined") ? "Another default" : optParam2;
            optParam3 = (typeof optParam3 === "undefined") ? "Some other default" : optParam3;
        }

        function TypeofFunction2(param1, optParam1, optParam2, optParam3) {
            optParam1 = defaultValue(optParam1, "Some default");
            optParam2 = defaultValue(optParam2, "Another default");
            optParam3 = defaultValue(optParam3, "Some other default");
        }

        function defaultValue(variable, defaultValue) {
            return (typeof variable !== 'undefined') ? (variable) : (defaultValue);
        }

        function SwitchFuntion(param1, optParam1, optParam2, optParam3) {
            switch (arguments.length - 1) { // <-- 1 is number of required arguments
                case 0:
                    optParam1 = 'Some default';
                case 1:
                    optParam2 = 'Another default';
                case 2:
                    optParam3 = 'Some other default';
            }
        }

        function ObjectFunction(args) {
            var defaults = {
                optParam1: 'Some default',
                optParam2: 'Another default',
                optParam3: 'Some other default'
            }
            args = $.extend({}, defaults, args);
        }

        function LogicalOrFunction(param1, optParam1, optParam2, optParam3) {
            optParam1 || (optParam1 = 'Some default');
            optParam2 || (optParam1 = 'Another default');
            optParam3 || (optParam1 = 'Some other default');
        }
    };
</script>
2
JDC

プロジェクトの間、私は自分自身がオプションのパラメータと設定を繰り返していたことに気づいたので、型チェックを扱い、デフォルト値を割り当てるクラスを作りました。例を見て、これがあなたのために働くかどうか私に知らせてください。

var myCar           = new Car('VW', {gearbox:'automatic', options:['radio', 'airbags 2x']});
var myOtherCar      = new Car('Toyota');

function Car(brand, settings) {
    this.brand      = brand;

    // readable and adjustable code
    settings        = DefaultValue.object(settings, {});
    this.wheels     = DefaultValue.number(settings.wheels, 4);
    this.hasBreaks  = DefaultValue.bool(settings.hasBreaks, true);
    this.gearbox    = DefaultValue.string(settings.gearbox, 'manual');
    this.options    = DefaultValue.array(settings.options, []);

    // instead of doing this the hard way
    settings        = settings || {};
    this.wheels     = (!isNaN(settings.wheels)) ? settings.wheels : 4;
    this.hasBreaks  = (typeof settings.hasBreaks !== 'undefined') ? (settings.hasBreaks === true) : true;
    this.gearbox    = (typeof settings.gearbox === 'string') ? settings.gearbox : 'manual';
    this.options    = (typeof settings.options !== 'undefined' && Array.isArray(settings.options)) ? settings.options : [];
}

このクラスを使う:

(function(ns) {

    var DefaultValue = {

        object: function(input, defaultValue) {
            if (typeof defaultValue !== 'object') throw new Error('invalid defaultValue type');
            return (typeof input !== 'undefined') ? input : defaultValue;
        },

        bool: function(input, defaultValue) {
            if (typeof defaultValue !== 'boolean') throw new Error('invalid defaultValue type');
            return (typeof input !== 'undefined') ? (input === true) : defaultValue;
        },

        number: function(input, defaultValue) {
            if (isNaN(defaultValue)) throw new Error('invalid defaultValue type');
            return (typeof input !== 'undefined' && !isNaN(input)) ? parseFloat(input) : defaultValue;
        },

        // wrap the input in an array if it is not undefined and not an array, for your convenience
        array: function(input, defaultValue) {
            if (typeof defaultValue === 'undefined') throw new Error('invalid defaultValue type');
            return (typeof input !== 'undefined') ? (Array.isArray(input) ? input : [input]) : defaultValue;
        },

        string: function(input, defaultValue) {
            if (typeof defaultValue !== 'string') throw new Error('invalid defaultValue type');
            return (typeof input === 'string') ? input : defaultValue;
        },

    };

    ns.DefaultValue = DefaultValue;

}(this));
2
Bart Wttewaall

未定義のテストは不要であり、それほど強力ではありません。user568458が指摘したように、nullまたはfalseが渡されると提供されるソリューションが失敗するためです。あなたのAPIのユーザはfalseまたはnullがメソッドにそのパラメータを避けることを強いると考えるかもしれません。

function PaulDixonSolution(required, optionalArg){
   optionalArg = (typeof optionalArg === "undefined") ? "defaultValue" : optionalArg;
   console.log(optionalArg);
};
PaulDixonSolution("required");
PaulDixonSolution("required", "provided");
PaulDixonSolution("required", null);
PaulDixonSolution("required", false);

年です。結果:

defaultValue
provided
null
false

最後の2つは潜在的に悪いです。代わりに試してみてください。

function bulletproof(required, optionalArg){
   optionalArg = optionalArg ? optionalArg : "defaultValue";;
   console.log(optionalArg);
};
bulletproof("required");
bulletproof("required", "provided");
bulletproof("required", null);
bulletproof("required", false);

結果は次のとおりです。

defaultValue
provided
defaultValue
defaultValue

これが最適ではない唯一のシナリオは、ブール値または意図的なNULLを意味するオプションのパラメータが実際にある場合です。

2
slartibartfast

OptionalArgが誤っている場合はすべてdefaultValueになります。

function myFunc(requiredArg, optionalArg) {
    optionalArg = optionalArg || 'defaultValue';
    console.log(optionalArg);
    // Do stuff
}
myFunc(requiredArg);
myFunc(requiredArg, null);
myFunc(requiredArg, undefined);
myFunc(requiredArg, "");
myFunc(requiredArg, 0);
myFunc(requiredArg, false);

上記のすべてのログdefaultValueは、6のすべてが偽造されているためです。ケース4、5、6では、optionalArgをdefaultValueに設定したくない場合がありますが、誤っているため設定されます。

1
Pavan Varanasi

これが私の解決策です。これであなたはあなたが望むどんなパラメータでも残すことができます。オプションのパラメータの順序は重要ではなく、カスタム検証を追加できます。

function YourFunction(optionalArguments) {
            //var scope = this;

            //set the defaults
            var _value1 = 'defaultValue1';
            var _value2 = 'defaultValue2';
            var _value3 = null;
            var _value4 = false;

            //check the optional arguments if they are set to override defaults...
            if (typeof optionalArguments !== 'undefined') {

                if (typeof optionalArguments.param1 !== 'undefined')
                    _value1 = optionalArguments.param1;

                if (typeof optionalArguments.param2 !== 'undefined')
                    _value2 = optionalArguments.param2;

                if (typeof optionalArguments.param3 !== 'undefined')
                    _value3 = optionalArguments.param3;

                if (typeof optionalArguments.param4 !== 'undefined')
                    //use custom parameter validation if needed, in this case for javascript boolean
                   _value4 = (optionalArguments.param4 === true || optionalArguments.param4 === 'true');
            }

            console.log('value summary of function call:');
            console.log('value1: ' + _value1);
            console.log('value2: ' + _value2);
            console.log('value3: ' + _value3);
            console.log('value4: ' + _value4);
            console.log('');
        }


        //call your function in any way you want. You can leave parameters. Order is not important. Here some examples:
        YourFunction({
            param1: 'yourGivenValue1',
            param2: 'yourGivenValue2',
            param3: 'yourGivenValue3',
            param4: true,
        });

        //order is not important
        YourFunction({
            param4: false,
            param1: 'yourGivenValue1',
            param2: 'yourGivenValue2',
        });

        //uses all default values
        YourFunction();

        //keeps value4 false, because not a valid value is given
        YourFunction({
            param4: 'not a valid bool'
        });
1
Duco L
  1. arg || 'default'は素晴らしい方法で、90%のケースで機能します

  2. 誤った値を渡す必要がある場合は失敗します。

    • false
    • 0
    • NaN
    • ""

    このような場合は、もう少し冗長でundefinedを確認する必要があります。

  3. また、最初にオプションの引数がある場合は注意してください。すべての引数の型に注意する必要があります。

1
mcfedr

人 -

これらと他の解決策を見た後、私はベースとしてW3Schoolsのコードの断片を使ってそれらのいくつかを試してみました。あなたは以下でうまくいくものを見つけることができます。各項目も同様にコメントアウトされていて、個々のコメントを削除するだけで実験できるようになっています。明確にするために、定義されていないのは "eyecolor"パラメータです。

function person(firstname, lastname, age, eyecolor)
{
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
this.eyecolor = eyecolor;
// if(null==eyecolor)
//   this.eyecolor = "unknown1";
//if(typeof(eyecolor)==='undefined') 
//   this.eyecolor = "unknown2";
// if(!eyecolor)
//   this.eyecolor = "unknown3";
this.eyecolor = this.eyecolor || "unknown4";
}

var myFather = new person("John", "Doe", 60);
var myMother = new person("Sally", "Rally", 48, "green");

var elem = document.getElementById("demo");
elem.innerHTML = "My father " +
              myFather.firstname + " " +
              myFather.lastname + " is " +
              myFather.age + " with " +
              myFather.eyecolor + " eyes.<br/>" +
              "My mother " +
              myMother.firstname + " " +
              myMother.lastname + " is " +
              myMother.age + " with " +
              myMother.eyecolor + " eyes."; 
1
Mark Funk

これが私が最後になったものです:

function WhoLikesCake(options) {
  options = options || {};
  var defaultOptions = {
    a : options.a || "Huh?",
    b : options.b || "I don't like cake."
  }
  console.log('a: ' + defaultOptions.b + ' - b: ' + defaultOptions.b);

  // Do more stuff here ...
}

このように呼ばれる:

WhoLikesCake({ b : "I do" });
1
NinjaFart
function Default(variable, new_value)
{
    if(new_value === undefined) { return (variable === undefined) ? null : variable; }
    return (variable === undefined) ? new_value : variable;
}

var a = 2, b = "hello", c = true, d;

var test = Default(a, 0),
test2 = Default(b, "Hi"),
test3 = Default(c, false),
test4 = Default(d, "Hello world");

window.alert(test + "\n" + test2 + "\n" + test3 + "\n" + test4);

http://jsfiddle.net/mq60hqrf/

1
Imagine Breaker

それらはtypeof演算子のバージョンよりも短いです。

function foo(a, b) {
    a !== undefined || (a = 'defaultA');
    if(b === undefined) b = 'defaultB';
    ...
}
0
Aurélien Ooms

私の間違いであれば訂正してください。しかし、これは最も単純な方法のようです(とにかく1つの引数に対して)。

function myFunction(Required,Optional)
{
    if (arguments.length<2) Optional = "Default";
    //Your code
}
0
Brian McCutchon