如何最好地确定参数是否未发送到 JavaScript 函数

IT技术 javascript arguments
2021-03-05 22:32:25

我现在已经看到了 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);
}
6个回答

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

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

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

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

测试undefined是下一个“最好的”——只有当函数被一个undefined显式调用时它才会“失败” ,在所有可能的情况下,应该像忽略参数一样对待它。

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

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

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

另一种提供默认值的好方法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
}

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

您实际上应该检查 typeof argument2 === "undefined",以防有人定义了 "undefined"。
2021-04-21 22:32:25
@Cristoph:阅读您的评论后,我四处询问。我能够证明字符串比较肯定不是(仅)通过指针比较,因为比较一个巨大的字符串比一个小字符串花费的时间更长。然而,比较两个巨大的字符串只有在它们是用串联方式构建时才真的很慢,但如果第二个是作为第一个的赋值创建的,则真的很快。所以它可能通过一个指针测试然后一个字母一个字母的测试来工作所以,是的,我充满了废话:)感谢你启发我......
2021-04-21 22:32:25
undefined 是全局空间中的一个变量。在全局范围内查找该变量比在局部范围内查找变量要慢。但最快的是使用 typeof x === "undefined"
2021-04-29 22:32:25
我会添加一个通知 - 但什么生病的混蛋会做这样的事情?
2021-05-08 22:32:25
有趣的。另一方面,比较 === undefined 可能比字符串比较快。我的测试似乎表明你是对的: x === undefined 需要 ~1.5x typeof x === 'undefined' 的时间
2021-05-10 22:32:25

如果您使用 jQuery,一个不错的选择(特别是对于复杂情况)是使用jQuery 的扩展方法

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"
};

这是我发现测试的少数情况之一:

if(! argument2) {  

}

效果很好,并且在语法上具有正确的含义。

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

编辑:

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

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

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

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

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

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

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

if(!argument2) argument2 = 'default' 等价于argument2 = argument2 || 'default' - 我发现第二个版本在视觉上更令人愉悦......
2021-04-16 22:32:25
我发现它更冗长和分散注意力;但这是个人喜好,我敢肯定。
2021-04-19 22:32:25
如果参数 2 是一个布尔值 === 假怎么办;或函数 {return false;} ?
2021-05-05 22:32:25
@le dorfier:除了美学之外,还有一个关键区别:后者有效地创建了第二条执行路径,这可能会诱使粗心的维护者在简单的默认值分配之外添加行为。YMMV,当然。
2021-05-11 22:32:25
@le dorfier:它还排除了使用空字符串、0 和布尔值 false。
2021-05-13 22:32:25

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

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

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

我的观点是您没有回答如何最好地确定参数是否未发送到 JavaScript 函数但是这个问题可以使用默认参数来回答:例如,用 命名你的参数"default value"并检查值是否确实是"default value"
2021-04-21 22:32:25
如我所见 - 如果未定义,他想定义 arg,所以我发布了一些有用的信息
2021-05-09 22:32:25
这个答案真的很有趣,可能对操作有用。虽然它并没有真正回答问题
2021-05-14 22:32:25

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

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, 和""转换为 boolean false。如果您要使用 Tom 的方法,则只有第四个测试将使用默认值!

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