JavaScript中有更好的可选函数参数的方式吗?

863

我一直是这样处理 JavaScript 中的可选参数:

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

  // Do stuff
}

有没有更好的方法?

在什么情况下使用像||这样的方式会失败?


10
http://www.openjs.com/articles/optional_function_arguments.php - slf
6
很有意思。将参数作为关联数组传递似乎是处理超过几个参数的好方法。 - Mark Biek
1
这正是我在大多数情况下所做的。我的具有超过1个参数的函数期望一个对象字面量。 - Andrew Hedges
2
@slf 我相信你的评论被忽略了,所以我为那些在谷歌上搜索的人添加了一个关于JavaScript中arguments的答案。 - Justus Romijn
2
参见:https://dev59.com/WXNA5IYBdhLWcg3wmfO5 - Malcolm
显示剩余2条评论
28个回答

6

我习惯于看到一些处理可选变量的基本变化。有时,放松版本很有用。

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 中广泛使用。

3

如果你正在使用Underscore库(你应该使用它,因为它是一个非常棒的库):

_.defaults(optionalArg, 'defaultValue');

2
降落到这个问题,搜索“EcmaScript 2015中的默认参数”,因此只是提到...

使用ES6,我们可以使用默认参数

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

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

2

在一个项目中,我发现自己在使用可选参数和设置时反复重复,因此我创建了一个类来处理类型检查并分配默认值,这样可以产生整洁且易读的代码。请参见示例,让我知道您是否认同。

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));

我喜欢这样做,我会走相似的路线。(我的唯一参数是一个具有多达20个属性的对象,其中可能有7个非常重要)。实际上,它不仅易读和易维护,而且还可重用和可扩展(例如添加回调/发出信号)。非常清晰,谢谢。 - Falk

2
我不知道为什么@Paul的回复被downvote了,但是对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

测试undefined是不必要的,也不够健壮,因为正如用户568458所指出的那样,如果传递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

最后两个可能不太好。建议尝试以下方式:

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

这种情况不是最优选择的唯一场景,是当您实际拥有可选参数且这些参数旨在是布尔值或有意的空值时。

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>

1
在所有可选参数为假的情况下,您将得到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,因为它们全部都是falsy。在情况4、5、6中,您可能不希望将optionalArg设置为defaultValue,但由于它们是falsy,所以它们被设置了。

1

这是我的解决方案。使用它,您可以留下任何想要的参数。可选参数的顺序不重要,您还可以添加自定义验证。

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
  1. arg || 'default' 是一个很好的方法,适用于90%的情况

  2. 当您需要传递可能是“falsy”的值时,它会失败

    • false
    • 0
    • NaN
    • ""

    对于这些情况,您需要更加详细地检查 undefined

  3. 还要注意当您首先有可选参数时,您必须了解所有参数的类型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接