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


236

现在,我已经看到了两种确定参数是否已传递给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);
}

根据Juan的评论,最好将Tom的建议更改为:

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

    alert(argument2);
}

真的一样 如果您总是有固定数量的参数,请使用第二种方法,否则使用arguments数组进行迭代可能会更容易。
卡·马蒂斯

7
未传递的参数为undefined。对null进行严格相等测试将失败。您应该对undefined使用严格的相等性。
Juan Mendes

19
请记住: argument2 || 'blah';如果argument2false(!),将导致'blah' ,而不仅仅是未定义的结果。 如果argument2为boolean,并且false为其传递了函数,则尽管argument2定义正确,该行仍将返回“ blah”
桑迪·吉福德

9
@SandyGifford:如果argument2是,或0,则存在相同的问题。''null
rvighne 2014年

@rvighne非常正确。Javascript对对象和强制转换的独特解释是它的最佳和最差部分。
桑迪·吉福德

Answers:


274

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

使用||运营商已经成为标准做法-所有时尚的年轻人做到这一点-但要小心:默认值将被触发,如果该参数的计算结果为false,这意味着它实际上可能是undefinednullfalse0''(或任何其他为它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
}

缺点是程序员的意图(在视觉上)不明显,而是使用“魔术数字”。因此,它可能容易出错。


42
实际上,您应该检查typeof参数2 ===“未定义”,以防有人定义“未定义”。
JW。

133
我会加个通知-但是,什么恶心的混蛋会那样做?
Christoph

12
undefined是全局空间中的变量。在全局范围内查找该变量比在本地范围内查找该变量要慢。但最快的方法是使用typeof运算X ===“未定义”
一些

5
有趣。另一方面,=== undefined比较可能比字符串比较快。我的测试似乎表明您是对的,尽管:x ===未定义需要〜1.5倍typeof的时间x ==='未定义'
Christoph

4
@克里斯托夫:看完你的评论后,我四处询问。我能够证明通过指针比较肯定不是(仅)字符串比较,因为比较巨大的字符串要比小的字符串花费更长的时间。但是,仅当两个巨大的字符串是通过串联建立起来的时,比较两个字符串才很慢,但是如果第二个字符串是从第一个字符串中创建的,则比较起来确实非常快。因此,它可能通过指针测试然后再逐字母测试的方式工作。是的,我满是垃圾:)感谢您的启发……
Juan Mendes

17

如果您使用的是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});

然后,options变量将是:

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

15

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

if(! argument2) {  

}

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

(由于同时存在限制,我不允许使用argument2具有其他含义的合法null值;但这确实很令人困惑。)

编辑:

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

我个人的偏爱(没有批评意味其他偏爱)是极简主义。只要我保持一致和简洁,代码要说的就越少,其他人也就越少理解正确地推断出我的意思。

这种偏好的一个含义是,我不想-觉得没有用-堆积了很多类型依赖测试。相反,我尝试使代码表示其含义。并只测试我真正需要测试的东西。

我在其他人的代码中发现的麻烦之一是需要弄清楚他们是否期望在更大的范围内实际遇到他们正在测试的案例。或者,如果他们试图测试所有可能的东西,则可能是他们没有完全完全预期上下文。这意味着我最终需要在两个方向上详尽地跟踪它们,然后才能放心地重构或修改任何内容。我认为他们很有可能进行了各种测试,因为他们预见了需要这些测试的环境(通常对我而言这并不明显)。

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

在将全面的ActionScript 3代码与优雅的javascript代码进行比较时,我最明显地看到了这一点。AS3可以是js的3到4倍,而我怀疑可靠性至少没有提高,这仅仅是因为所做出的编码决策数量(3-4X)。

如您所说,Shog9,YMMV。:D


1
if(!argument2)arguments2 ='default'等效于arguments2 = arguments2 || '默认'-我觉得第二个版本在视觉上更令人愉快……
Christoph

5
而且我发现它更加冗长和分散注意力;但我确定这是个人喜好。
dkretz

4
@le dorfier:它还排除了使用空字符串,0和布尔值false的情况。
Shog9年

@le dorfier:除了美学之外,还有一个主要区别:后者有效地创建了第二条执行路径,这可能会诱使粗心的维护者添加行为,而不是简单地分配默认值。YMMV,当然。
Shog9,2009年

3
如果parameter2是布尔值=== false,该怎么办?或函数{return false;}?
fresko

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将转换undefinednull0,和""成布尔false。如果你使用汤姆的方法,只有第四次试验将使用默认的!

您选择哪种方法实际上取决于您的预期行为。如果undefined不允许使用以外的值argument2,那么您可能会希望第一个有所不同;如果需要一个非零,非空,非空的值,那么第二种方法是理想的-实际上,它经常被用来快速消除考虑范围如此广的值。


7
url = url === undefined ? location.href : url;

裸露的骨头回答。一些解释不会有任何伤害。
Paul Rooney

它是一个三元运算符,简而言之:如果url未定义(丢失),则将url变量设置为location.href(当前网页),否则将url变量设置为已定义的url。
rmooney19年

5

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

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

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


这个答案真的很有趣,可能对操作很有用。虽然它并没有真正回答这个问题
雅典BN

如我所见-他想定义arg(如果未定义的话),所以我发布了一些有用的信息
Andriy2

我的意思是,您没有回答如何最佳确定是否未将参数发送给JavaScript函数。但是可以使用默认参数来回答这个问题:例如,使用命名您的参数"default value"并检查该值是否确实是"default value"
Ulysse BN

4

抱歉,我仍然无法评论,所以要回答汤姆的答案...在javascript中(未定义!= null)== false实际上,该函数无法使用“ null”,您应该使用“ undefined”


1
汤姆(Tom)给出了错误答案的两次投票-知道这些社区系统的运作情况总是很高兴;)
Christoph

3

为什么不使用!!运算符?该运算符位于变量之前,将其转换为布尔值(如果我理解得很好),所以!!undefinedand !!null(甚至!!NaN,这可能很有趣)将返回false

这是一个例子:

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

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

foo() //=> will log false

不适用于布尔值true,零和空字符串。例如foo(0); 将记录为false,但是foo(1)将记录为true
rosell.dk

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

2

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

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

这是必需的,这意味着如果value不存在/不传递的值-将其设置为0。

太酷了吧!


1
这实际上意味着“如果value等于false,将其设置为0”。这是一个微妙但非常重要的区别。
查尔斯伍德

@CharlesWood值未通过/当前表示仅是错误
Mohammed Zameer '18年

1
当然,如果这符合您的功能要求。但是,例如,如果您的参数是布尔值,则truefalse是有效值,并且当根本不传递参数时(尤其是函数具有多个参数时),您可能希望具有第三种行为。
查尔斯·伍德

1
我应该承认,这是计算机科学中的一个巨大论据,并且最终可能只是一个见解:D
Charles Wood,

@CharlesWood很抱歉迟到了聚会。我建议您使用编辑选项在答案中添加这些内容
Mohammed Zameer

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

0
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。考虑让世界变得更美好。


-1

fnCalledFunction(Param1,Param2,window.YourOptionalParameter)

如果从许多地方调用了上述函数,并且您确定从每个位置都传递了前两个参数,但是不确定第三个参数,则可以使用window。

如果未通过调用方方法定义window.param3,则它将进行处理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.