说明
JavaScript 中没有真正意义上的函数重载。
函数重载
函数名相同,函数的参数列表不同(包括参数个数和参数类型),根据参数的不同去执行不同的操作。
我们举个例子看看:
function overload(a){
console.log('一个参数')
}
function overload(a,b){
console.log('两个参数')
}
// 在支持重载的编程语言中,比如 java
overload(1); //一个参数
overload(1,2); //两个参数
// 在 JavaScript 中
overload(1); //两个参数
overload(1,2); //两个参数
在JavaScript中,同一个作用域,出现两个名字一样的函数,后面的会覆盖前面的,所以 JavaScript 没有真正意义的重载。
但是有各种办法,能在 JavaScript 中模拟实现重载的效果。
第一种方法,通过 arguments 对象来实现
function overLoading() {
// 根据arguments.length,对不同的值进行不同的操作
switch(arguments.length) {
case 0:
/* 操作1的代码写在这里 */
break;
case 1:
/* 操作2的代码写在这里 */
break;
case 2:
/* 操作3的代码写在这里 */
// 后面还有很多的case......
}
}
这个例子非常简单,就是通过判断 arguments 对象的 length 属性来确定有几个参数,然后执行什么操作。
但是参数少的情况下,还好,如果参数多一些,if 判断就需要写好多,就麻烦了。
第二种方法,通过闭包来实现
在看这个例子之前,我们先来看一个需求,我们有一个 users
对象,users
对象的values
属性中存着一些名字。一个名字由两部分组成,空格左边的是 first-name ,空格右边的是 last-name,像下面这样。
const users = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
我们要在 users
对象 中添加一个 find 方法,
当不传任何参数时, 返回整个users.values
;
当传一个参数时,就把 first-name 跟这个参数匹配的元素返回;
当传两个参数时,则把 first-name 和 last-name 都匹配的返回。
这个需求中 find
方法 需要根据参数的个数不同而执行不同的操作,下来我们通过一个 addMethod
函数,来在 users 对象中添加这个find
方法。
function addMethod (object, name, fn) {
// 先把原来的object[name] 方法,保存在old中
const old = object[name];
// 重新定义 object[name] 方法
object[name] = function () {
// 如果函数需要的参数 和 实际传入的参数 的个数相同,就直接调用fn
if (fn.length === arguments.length) {
return fn.apply(this, arguments);
// 如果不相同,判断old 是不是函数,
// 如果是就调用old,也就是刚才保存的 object[name] 方法
} else if (typeof old === "function") {
return old.apply(this, arguments);
}
}
}
addMethod 函数,它接收3个参数
第一个:要绑定方法的对象,
第二个:绑定的方法名称,
第三个:需要绑定的方法
addMethod
函数是利用了闭包的特性,通过变量 old
将每个函数连接了起来,让所有的函数都留在内存中
全部代码
const users = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
function addMethod (object, name, fn) {
// 先把原来的object[name] 方法,保存在old中
const old = object[name];
// 重新定义 object[name] 方法
object[name] = function () {
// 如果函数需要的参数 和 实际传入的参数 的个数相同,就直接调用fn
if (fn.length === arguments.length) {
return fn.apply(this, arguments);
// 如果不相同,判断old 是不是函数,
// 如果是就调用old,也就是刚才保存的 object[name] 方法
} else if (typeof old === "function") {
return old.apply(this, arguments);
}
}
}
// 不传参数时,返回整个values数组
function find0 () {
return this.values;
}
// 传一个参数时,返回firstName匹配的数组元素
function find1 (firstName) {
var ret = [];
for (var i = 0; i < this.values.length; i++) {
if (this.values[i].indexOf(firstName) === 0) {
ret.push(this.values[i
]);
}
}
return ret;
}
// 传两个参数时,返回firstName和lastName都匹配的数组元素
function find2 (firstName, lastName) {
var ret = [];
for (var i = 0; i < this.values.length; i++) {
if (this.values[i
] === (firstName + " " + lastName)) {
ret.push(this.values[i
]);
}
}
return ret;
}
// 给 users 对象添加处理 没有参数 的方法
addMethod(users, "find", find0);
// 给 users 对象添加处理 一个参数 的方法
addMethod(users, "find", find1);
// 给 users 对象添加处理 两个参数 的方法
addMethod(users, "find", find2);
console.dir(users.find);
// 测试:
console.log(users.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(users.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(users.find("Dean","Edwards")); //["Dean Edwards"]
每调用一次 addMethod
函数,就会产生一个 old
,形成一个闭包。
我们可以通过 console.dir(users.find)
,把 find
方法打印到控制台看看
上面的例子,本质都是在判断参数的个数,根据不同的个数,执行不同的操作,而下来举的例子是通过判断参数的类型,来执行不同的操作。
我们看看 jQuery 中的 css( ) 方法。
css( ) 方法返回或设置匹配的元素的一个或多个样式属性。
css(name|pro|[,val|fn])
我们可以看到 css( ) 方法,有5种 参数情况,其中3种是一个参数,另外两种是两个参数。
而在只有一个参数的情况下,如果参数类型是字符串或者数组就是获取属性值,而如果参数是对象,就是是设置属性值。
jQuery 的 css( ) 方法就是通过判断参数的类型,来确定执行什么操作。
我们来看看jQuery 3.3.1中的源码
// name 表示属性名
// value 表示属性值
css: function( name, value ) {
return access( this, function( elem, name, value ) {
var styles, len,
map = {},
i = 0;
// 判断属性名是不是数组
// 是数组就遍历,调用jQuery.css 方法传入每个属性名,获取样式
if ( Array.isArray( name ) ) {
styles = getStyles( elem );
len = name.length;
for ( ; i < len; i++ ) {
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
}
return map;
}
// 如果value 不等于 undefined 就调用jQuery.style 方法设置样式
// 如果value 等于 undefined 就调用jQuery.css 方法获取样式
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
}, name, value, arguments.length > 1 );
}
css( ) 方法依赖于三个方法:
1、jQuery.access( ) 方法,这个方法可以获取 或 设置,一个或者多个属性值
jQuery.access( ) 方法里有这样的代码
// 设置多个属性值
// 如果属性名(key)的类型是 object,就遍历这个对象
// 遍历一次就调用一次 access()方法,并传入这次的属性名和属性值
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
}
// 设置一个值
} else if ( value !== undefined ) {
......
}
也就是这个方法,在帮 css( ) 方法判断第一个参数是字符串还是对象的。
2、jQuery.style( ) 方法:在DOM节点上读取或设置样式属性
在css( )方法中,如果有传第二个参数,也就是有要设置的属性值时,那就会调用 jQuery.style( ) 方法设置样式
3、jQuery.css( ) :在DOM元素上读取DOM样式值
这里的 jQuery.css( ) 是通过 jQuery.extend( )
添加的方法,而我们最开始提到的 css( )方法,是通过 jQuery.fn.extend( )
添加的方法,他们不是同一个方法。
jQuery.extend( )与 jQuery.fn.extend( )的区别
jQuery.extend( )是为jQuery类添加类方法(静态方法),需要通过jQuery类来调用(直接使用 $.xxx 调用);
jQuery.fn.extend( )是为jQuery类添加成员数(实例方法),所有jQuery实例都可以直接调用(需要使用 $().xxx 调用)。
重载的好处
重载其实是把多个功能相近的函数合并为一个函数,重复利用了函数名。
假如jQuery中的css( )方法不使用 重载,那么就要有5个不同的函数,来完成功能,那我们就需要记住5个不同的函数名,和各个函数相对应的参数的个数和类型,显然就麻烦多了。
总结
虽然 JavaScript 并没有真正意义上的重载,但是重载的效果在JavaScript中却非常常见,比如 数组的 splice( )方法,一个参数可以删除,两个参数可以删除一部分,三个参数可以删除完了,再添加新元素。
再比如 parseInt( )方法 ,传入一个参数,就判断是用十六进制解析,还是用十进制解析,如果传入两个参数,就用第二个参数作为数字的基数,来进行解析。
文中提到的实现重载效果的方法,本质都是对参数进行判断,不管是判断参数个数,还是判断参数类型,都是根据参数的不同,来决定执行什么操作的。
虽然,重载能为我们带来许多的便利,但是也不能滥用,不要把一些根本不相关的函数合为一个函数,那样并没有什么意义。
产考
浅谈JavaScript函数重载
JavaScript函数重载
JavaScript中的函数重载(Function overloading)
网友评论