Javascript 中的参数重载有多个方面:
-
变量参数 - 您可以传递不同的参数集(类型和数量),函数的行为方式将与传递给它的参数相匹配。
-
默认参数 - 如果未传递参数,则可以为参数定义默认值。
-
命名参数 - 参数顺序变得无关紧要,您只需命名要传递给函数的参数即可。
以下是有关这些参数处理类别的章节。
变量参数
因为javascript没有对参数进行类型检查或所需的参数数量,所以你可以只拥有一个实现,可以通过检查参数的类型,存在或数量来适应传递给它的参数。myFunc()
jQuery一直在这样做。您可以将某些参数设置为可选参数,也可以根据传递给函数的参数在函数中进行分支。
在实现这些类型的重载时,您可以使用几种不同的技术:
- 您可以通过检查声明的参数名称值是否为 来检查是否存在任何给定参数。
undefined
- 您可以使用 检查总数量或参数。
arguments.length
- 您可以检查任何给定参数的类型。
- 对于可变数量的参数,可以使用伪数组访问带有 的任何给定参数。
arguments
arguments[i]
以下是一些示例:
让我们看一下jQuery的方法。它支持四种不同的使用形式:obj.data()
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
每个触发不同的行为,并且在不使用这种动态形式的重载的情况下,将需要四个单独的函数。
以下是如何用英语辨别所有这些选项,然后我将在代码中将它们全部组合在一起:
// get the data element associated with a particular key value
obj.data("key");
如果传递给的第一个参数是字符串,而第二个参数是 ,则调用方必须使用此形式。.data()
undefined
// set the value associated with a particular key
obj.data("key", value);
如果第二个参数不是未定义的,则设置特定键的值。
// get all keys/values
obj.data();
如果未传递任何参数,则返回返回对象中的所有键/值。
// set all keys/values from the passed in object
obj.data(object);
如果第一个参数的类型是普通对象,则设置该对象的所有键/值。
以下是如何将所有这些组合到一组javascript逻辑中:
// method declaration for .data()
data: function(key, value) {
if (arguments.length === 0) {
// .data()
// no args passed, return all keys/values in an object
} else if (typeof key === "string") {
// first arg is a string, look at type of second arg
if (typeof value !== "undefined") {
// .data("key", value)
// set the value for a particular key
} else {
// .data("key")
// retrieve a value for a key
}
} else if (typeof key === "object") {
// .data(object)
// set all key/value pairs from this object
} else {
// unsupported arguments passed
}
},
此技术的关键是确保要接受的所有形式的参数都是唯一可识别的,并且永远不会对调用方使用哪种形式产生任何混淆。这通常需要对参数进行适当的排序,并确保参数的类型和位置具有足够的唯一性,以便您始终可以分辨出正在使用哪种形式。
例如,如果您有一个采用三个字符串参数的函数:
obj.query("firstArg", "secondArg", "thirdArg");
您可以轻松地将第三个参数设置为可选,并且可以轻松检测该条件,但是您不能仅将第二个参数设置为可选,因为您无法判断调用方意味着传递其中的哪一个,因为无法确定第二个参数是第二个参数还是省略了第二个参数,因此第二个参数的位置实际上是第三个参数:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
由于所有三个参数都是同一类型,因此您无法区分不同参数,因此您不知道调用方的意图。使用此调用样式时,只有第三个参数可以是可选的。如果要省略第二个参数,则必须将其作为(或其他一些可检测值)传递,并且您的代码将检测到:null
obj.query("firstArg", null, "thirdArg");
下面是可选参数的 jQuery 示例。这两个参数都是可选的,如果未传递,则采用默认值:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
下面是一个jQuery示例,其中参数可以丢失,或者三种不同类型的任何一种,它为您提供了四种不同的重载:
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
命名参数
其他语言(如Python)允许传递命名参数,作为仅传递某些参数并使参数独立于传递顺序的方法。Javascript 不直接支持命名参数的功能。通常用于其位置的设计模式是传递属性/值的映射。这可以通过传递具有属性和值的对象来完成,或者在ES6及更高版本中,您实际上可以传递Map对象本身。
下面是一个简单的 ES5 示例:
jQuery's接受一种用法形式,您只需向它传递一个参数,该参数是具有属性和值的常规Javascript对象。您传递给它的属性决定了哪些参数/选项被传递给 ajax 调用。有些可能是必需的,许多是可选的。由于它们是对象上的属性,因此没有特定的顺序。实际上,可以在该对象上传递 30 多个不同的属性,只需要一个(url)。$.ajax()
下面是一个示例:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
在实现内部,它可以只询问在传入对象上传递了哪些属性,并将这些属性用作命名参数。这可以通过将所有属性放入数组中,然后迭代该数组来完成。$.ajax()
for (prop in obj)
Object.keys(obj)
这种技术在Javascript中非常常用,当有大量参数和/或许多参数是可选的时。注意:这给实现函数带来了责任,以确保存在最小的有效参数集,并为调用方提供一些调试反馈,如果传递的参数不足(可能是通过抛出带有有用错误消息的异常)。
在ES6环境中,可以使用解构来为上述传递的对象创建默认属性/值。本参考文章对此进行了更详细的讨论。
以下是该文章的一个示例:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
然后,您可以像以下任何一种一样调用它:
selectEntries({start: 5});
selectEntries({start: 5, end: 10});
selectEntries({start: 5, end: 10, step: 2});
selectEntries({step: 3});
selectEntries();
未在函数调用中列出的参数将从函数声明中获取其默认值。
这将为 传递给函数的对象的 和 属性创建默认属性和值。start
end
step
selectEntries()
函数参数的默认值
在 ES6 中,Javascript 添加了对参数默认值的内置语言支持。
例如:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
在MDN上可以在这里使用的方式的进一步描述。