我已经看到了两种方法,用于确定参数是否已传递给JavaScript函数。我想知道是一种方法比另一种好,还是一种方法不好用?
function Test(argument1, argument2) {
if (Test.arguments.length == 1) argument2 = 'blah';
alert(argument2);
}
Test('test');
Or
function Test(argument1, argument2) {
argument2 = argument2 || 'blah';
alert(argument2);
}
Test('test');
据我所知,它们的结果是一样的,但我以前在生产中只使用过第一个。
Tom提到的另一个选择:
function Test(argument1, argument2) {
if(argument2 === null) {
argument2 = 'blah';
}
alert(argument2);
}
根据Juan的评论,Tom的建议最好改为:
function Test(argument1, argument2) {
if(argument2 === undefined) {
argument2 = 'blah';
}
alert(argument2);
}
有几种不同的方法来检查参数是否传递给了函数。除了你在你的(原始的)问题检查论点中提到的两个。长度或使用||操作符提供默认值-如果你是偏执狂,你也可以通过argument2 === undefined或typeof argument2 === 'undefined'显式检查参数是否为undefined(见注释)。
使用||操作符已经成为标准实践——所有很酷的孩子都这么做——但要小心:如果参数的计算结果为false,则会触发默认值,这意味着它实际上可能是未定义的、null、false、0,”(或任何Boolean(…)返回false的值)。
所以问题是什么时候使用哪种检查,因为它们产生的结果都略有不同。
检查参数。长度表现出“最正确”的行为,但如果有多个可选参数,它可能就不可行了。
对于undefined的测试是下一个“最好”的测试——只有当函数显式地使用一个未定义的值调用时,它才会“失败”,这很可能与省略实参一样。
使用||操作符可能会触发使用默认值,即使提供了有效参数。另一方面,它的行为实际上可能是需要的。
总结一下:只有在你知道自己在做什么的时候才使用它!
在我看来,使用||也是一种方法,如果有多个可选参数,并且不想传递一个对象文字作为命名参数的变通方法。
另一种使用参数提供默认值的好方法。长度可以通过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
}
这样做的缺点是,程序员的意图(在视觉上)并不明显,而是使用“神奇的数字”;因此,它可能容易出错。
这是我发现的少数几个测试案例之一:
if(! argument2) {
}
工作得很好,并且在语法上具有正确的含义。
(同时限制我不允许argument2有一个合法的空值,它有一些其他的含义;但这真的会让人困惑。)
编辑:
这是一个很好的例子,说明了松散类型语言和强类型语言之间的风格差异;这是javascript提供的一种风格选择。
我个人的偏好是极简主义(对其他偏好没有批评)。只要我是一致和简洁的,代码需要表达的越少,其他人就需要理解的越少,从而正确地推断出我的意思。
这种偏好的一个暗示是,我不想——也不认为它有用——堆积一堆类型依赖的测试。相反,我试图让代码的意思像它看起来的那样;只测试我真正需要测试的东西。
One of the aggravations I find in some other peoples' code is needing to figure out whether or not they expect, in the larger context, to actually run into the cases they are testing for. Or if they are trying to test for everything possible, on the chance that they don't anticipate the context completely enough. Which means I end up needing to track them down exhaustively in both directions before I can confidently refactor or modify anything. I figure that there's a good chance they might have put those various tests in place because they foresaw circumstances where they would be needed (and which usually aren't apparent to me).
(我认为这是这些人使用动态语言的一个严重缺点。人们经常不愿意放弃所有的静态测试,最终以假装测试而告终。)
在比较全面的ActionScript 3代码和优雅的javascript代码时,我最清楚地看到了这一点。AS3可以是js的3到4倍,我怀疑可靠性至少不会更好,只是因为所做的编码决策的数量(3-4倍)。
如你所说,Shog9, YMMV。: D
两者存在显著差异。让我们建立一些测试用例:
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和""转换为布尔值false。如果您使用Tom的方法,那么只有第四个测试将使用默认的方法!
你选择哪种方法实际上取决于你的预期行为。如果参数2允许使用undefined以外的值,那么你可能需要对第一个参数进行一些更改;如果需要一个非零、非空、非空的值,那么第二种方法是理想的——实际上,它经常被用来快速地从考虑中消除如此广泛的值。
通过调用带有可选属性的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]; }
}
}
}
}
如果您正在使用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"
};
有时你想要undefined作为一个可能的参数,但你仍然有可能不传递参数的情况。在这种情况下,你可以使用参数。长度来检查传递了多少个参数。
// 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为undefined
testField('id', 12, undefined) ->错误,我们希望id是undefined