256

我现在已经看到了 2 种方法来确定参数是否已传递给 JavaScript 函数。我想知道一种方法是否比另一种更好,或者一种方法不好用?

 function Test(argument1, argument2) {
      if (Test.arguments.length == 1) argument2 = 'blah';

      alert(argument2);
 }

 Test('test');

或者

 function Test(argument1, argument2) {
      argument2 = argument2 || 'blah';

      alert(argument2);
 }

 Test('test');

据我所知,它们都会产生相同的结果,但我之前只在生产中使用过第一个。

汤姆提到的另一个选项:

function Test(argument1, argument2) {
    if(argument2 === null) {
        argument2 = 'blah';
    }

    alert(argument2);
}

根据胡安的评论,最好将汤姆的建议改为:

function Test(argument1, argument2) {
    if(argument2 === undefined) {
        argument2 = 'blah';
    }

    alert(argument2);
}
4

14 回答 14

290

有几种不同的方法可以检查参数是否传递给函数。除了您在(原始)问题中提到的两个 - 检查arguments.length或使用||运算符提供默认值 - 还可以显式检查参数是否undefined通过argument2 === undefinedtypeof argument2 === 'undefined'是否偏执(见评论)。

使用||运算符已成为标准做法 - 所有酷孩子都这样做 - 但要小心:如果参数评估为 ,则将触发默认值false,这意味着它实际上可能是undefined, null, false, 0, ''(或任何其他Boolean(...)返回false)。

所以问题是何时使用哪种检查,因为它们都会产生略有不同的结果。

检查arguments.length表现出“最正确”的行为,但如果有多个可选参数,则可能不可行。

测试undefined是下一个“最好的” - 如果函数是用一个undefined值显式调用的,它只会“失败”,这很可能应该被视为与省略参数相同的方式。

||即使提供了有效参数,使用运算符也可能触发使用默认值。另一方面,它的行为实际上可能是合乎需要的。

总结一下:只有在你知道自己在做什么的情况下才使用它!

在我看来,||如果有多个可选参数并且不想传递对象文字作为命名参数的解决方法,那么 using 也是一种方法。

另一种提供默认值的好方法arguments.length是通过 switch 语句的标签:

function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
    switch(arguments.length) {
        case 1: optionalArg1 = 'default1';
        case 2: optionalArg2 = 'default2';
        case 3: optionalArg3 = 'default3';
        case 4: break;
        default: throw new Error('illegal argument count')
    }
    // do stuff
}

这样做的缺点是程序员的意图(视觉上)不明显,并且使用了“幻数”;因此,它可能容易出错。

于 2009-01-04T17:46:09.197 回答
18

如果您使用 jQuery,一个不错的选择(尤其是对于复杂的情况)是使用jQuery 的 extend 方法

function foo(options) {

    default_options = {
        timeout : 1000,
        callback : function(){},
        some_number : 50,
        some_text : "hello world"
    };

    options = $.extend({}, default_options, options);
}

如果您调用该函数,则如下所示:

foo({timeout : 500});

选项变量将是:

{
    timeout : 500,
    callback : function(){},
    some_number : 50,
    some_text : "hello world"
};
于 2013-04-30T18:16:37.493 回答
16

这是我找到测试的少数情况之一:

if(! argument2) {  

}

工作得很好,并且在语法上具有正确的含义。

(同时限制我不允许argument2具有其他含义的合法空值;但这真的很令人困惑。)

编辑:

这是松散类型语言和强类型语言之间风格差异的一个很好的例子;以及 javascript 提供的一种风格选择。

我个人的偏好(没有针对其他偏好的批评)是极简主义。代码要说的越少,只要我保持一致和简洁,其他人就越不需要理解才能正确推断出我的意思。

这种偏好的一个含义是我不想 - 不觉得它有用 - 堆积一堆类型依赖性测试。相反,我尝试使代码具有它看起来的含义;并且只测试我真正需要测试的东西。

我在其他一些人的代码中发现的恶化之一是需要弄清楚他们是否期望在更大的上下文中实际遇到他们正在测试的案例。或者,如果他们试图测试所有可能的东西,可能是因为他们对上下文的预测不够充分。这意味着我最终需要在两个方向上彻底追踪它们,然后才能自信地重构或修改任何东西。我认为他们很有可能已经进行了各种测试,因为他们预见到了需要它们的情况(而这通常对我来说并不明显)。

(我认为这些人使用动态语言的方式有一个严重的缺点。很多时候人们不想放弃所有的静态测试,并最终伪造它。)

在比较全面的 ActionScript 3 代码和优雅的 javascript 代码时,我最明显地看到了这一点。AS3 可能是 js 体积的 3 或 4 倍,而且我怀疑可靠性至少不会更好,这仅仅是因为做出的编码决策数量(3-4X)。

正如你所说,Shog9,YMMV。:D

于 2009-01-04T17:53:23.830 回答
10

在 ES6 (ES2015) 中,您可以使用默认参数

function Test(arg1 = 'Hello', arg2 = 'World!'){
  alert(arg1 + ' ' +arg2);
}

Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!

于 2017-07-23T22:14:00.870 回答
7

存在显着差异。让我们设置一些测试用例:

var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");

使用您描述的第一种方法,只有第二个测试将使用默认值。第二种方法将默认除第一种以外的所有方法(因为 JS 会将undefined, null, 0, and""转换为 boolean false。如果您要使用 Tom 的方法,则只有第四种测试将使用默认值!

您选择哪种方法实际上取决于您的预期行为。如果undefined允许的值不是argument2,那么您可能需要对第一个值进行一些更改;如果需要一个非零、非空、非空值,那么第二种方法是理想的——实际上,它通常用于快速消除考虑范围内的如此广泛的值。

于 2009-01-04T17:57:44.197 回答
7
url = url === undefined ? location.href : url;
于 2012-08-21T09:51:08.540 回答
4

对不起,我还不能发表评论,所以回答汤姆的回答......在 javascript (undefined != null) == false 实际上该函数不适用于“null”,您应该使用“undefined”

于 2009-01-04T17:48:14.577 回答
3

为什么不使用!!运算符?这个操作符,放在变量之前,把它变成一个布尔值(如果我理解得很好的话),所以!!undefinedand !!null(甚至!!NaN,这可能很有趣)将返回false

这是一个例子:

function foo(bar){
    console.log(!!bar);
}

foo("hey") //=> will log true

foo() //=> will log false
于 2015-12-30T13:01:58.737 回答
3

还有一个棘手的方法来查找参数是否传递给函数。看看下面的例子:

this.setCurrent = function(value) {
  this.current = value || 0;
};

这意味着如果 的值value不存在/未通过 - 将其设置为 0。

很酷哈!

于 2017-03-24T18:44:49.697 回答
2

通过使用可选属性的 Object 调用函数可以方便地进行参数检测:

function foo(options) {
    var config = { // defaults
        list: 'string value',
        of: [a, b, c],
        optional: {x: y},
        objects: function(param){
           // do stuff here
        }
    }; 
    if(options !== undefined){
        for (i in config) {
            if (config.hasOwnProperty(i)){
                if (options[i] !== undefined) { config[i] = options[i]; }
            }
        }
    }
}
于 2013-03-05T02:22:31.240 回答
2

有时您希望 undefined 作为可能的参数,但您仍然会遇到无法传递参数的情况。在这种情况下,您可以使用arguments.length来检查传递了多少参数。


// Throw error if the field is not matching our expectations
function testField(label, fieldValue, expectedValue) {
    console.log(arguments) // Gives: [Arguments] { '0': 'id', '1': 1, '2': undefined }
    if(arguments.length === 2) {
        if(!fieldValue) {
            throw new Error(`Field "${label}" must have a value`)
        }
    }

    else if(expectedValue === undefined) {
        if(fieldValue !== undefined) {
            throw Error(`Field "${label}" must NOT have a value`)
        }
    }

    // We stringify so our check works for objects as well
    else {
        if(JSON.stringify(fieldValue) !== JSON.stringify(expectedValue)) {
            throw Error(`Field "${label}" must equal ${expectedValue} but was ${fieldValue}`)
        }
    }
}

testField('id', 12)-> 通过,我们不希望 id 为空白

testField('id', undefined, undefined)-> 通过,我们希望 id 未定义

testField('id', 12, undefined)-> 错误,我们希望 id 未定义

于 2021-08-30T12:16:20.003 回答
1

有时您可能还想检查类型,特别是如果您将函数用作 getter 和 setter。以下代码是 ES6(不会在 EcmaScript 5 或更早版本中运行):

class PrivateTest {
    constructor(aNumber) {
        let _aNumber = aNumber;

        //Privileged setter/getter with access to private _number:
        this.aNumber = function(value) {
            if (value !== undefined && (typeof value === typeof _aNumber)) {
                _aNumber = value;
            }
            else {
                return _aNumber;
            }
        }
    }
}
于 2016-05-18T19:06:28.060 回答
1
function example(arg) {
  var argumentID = '0'; //1,2,3,4...whatever
  if (argumentID in arguments === false) {
    console.log(`the argument with id ${argumentID} was not passed to the function`);
  }
}

因为数组继承自Object.prototype. 考虑⇑让世界变得更美好。

于 2020-01-02T03:13:17.427 回答
-1

fnCalledFunction(Param1,Param2, window.YourOptionalParameter)

如果上面的函数是从很多地方调用的,并且你确定前 2 个参数是从每个地方传递的,但不确定第三个参数,那么你可以使用 window.

如果没有从调用者方法中定义,window.param3 将处理。

于 2018-06-28T05:56:06.497 回答