在面向对象语言中, 重载就是一组具有相同名字、不同参数列表的函数,但在js中,重载的实现不能像js一样那么简单,因为js名字相同的函数会相互覆盖,这样只能输出最后一个同名函数,哪怕参数不一样,那我们怎样才能做到重载呢,
首先我们可以利用js的函数中一个参数,arguments,该参数有个属性,length, 根据参数组的长度不同,我们可以做出相应的动作,从而达到重载的目的
function add(){
var len = arguments.length;
var a = arguments[0];
var b = arguments[1];
var c = arguments[2];
switch(len){
case 1:
console.log(a);
break;
case 2:
console.log(a+b);
break;
case 3:
console.log(a+b+c);
break;
default:
break;
}
}
add(1); //1
add(1,2); //3
add(1,2,3); // 6
如上面的代码,如果我们将add 函数传进来的参数进行判断,如果与条件相符,我们就做出相应的动作,这种方法通俗易懂,如果以后面试问道重载可以回答一下,但这样代码的利用性太低,多个函数需要重复多次,我们下面讲一个JQuery之父John Resig写的重载
function addMethod(object, name, fn) {
var old = object[name];
object[name] = function() {
console.log(arguments);
console.log(fn.length);
if(fn.length === arguments.length) {
return fn.apply(this, arguments);
} else if(typeof old === "function") {
return old.apply(this, arguments);
}
}
console.log(object[name]);
}
这是一个关键的方法,他有三个参数,第一个是指定的对象,第二个是指定对象要重载的方法,第三个是实际重载方法中要执行的方法,这样说有点绕口,下面来看代码
var people = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
addMethod(people, "find", function() {
return this.values;
});
// 这里进行第一次函数绑定,对people这个对象进行绑定find函数,我们根据上面的代码可知,此时的old 为undefined
此时的people.find()被构造成一下函数
function() {
console.log(arguments);
console.log(fn.length);
if(fn.length === arguments.length) {
return fn.apply(this, arguments);
} else if(typeof old === "function") {
return old.apply(this, arguments);
}
}
然后我们进行第二次绑定
addMethod(people, "find", function(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;
});
此时进行绑定时,old 变量这时候就变为了第一个people.find();函数,那么下次被调用时,第一个addMethod所绑定的people.find()函数就会存在内存中,这里巧妙的利用了闭包,将三个find函数保存在不同的内存空间中,其中的old变量会指向上一个的people.find函数
从而,一直往上访问,知道访问到第一个的old ,下面是完整的代码。
function addMethod(object, name, fn) {
var old = object[name];
object[name] = function() {
console.log(arguments);
console.log(fn.length);
if(fn.length === arguments.length) {
return fn.apply(this, arguments);
} else if(typeof old === "function") {
return old.apply(this, arguments);
}
}
console.log(object[name]);
}
var people = {
values: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
/* 下面开始通过addMethod来实现对people.find方法的重载 */
// 不传参数时,返回peopld.values里面的所有元素
addMethod(people, "find", function() {
return this.values;
});
// 传一个参数时,按first-name的匹配进行返回
addMethod(people, "find", function(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;
});
// 传两个参数时,返回first-name和last-name都匹配的元素
addMethod(people, "find", function(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;
});
// 测试:
console.log(people.find()); //["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); //["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean","Edwards")); //["Dean Edwards"]*/
当我们调用people.find();时会首先在addMethod所绑定的第三个people.find()中寻找,此时,fn.length就是期望函数的参数,而arguments.length是我们调用函数的参数,第一个调用函数的参数为0;而第三个所绑定的find函数参数为2个,所以不相等,我们就会向上找
old的所指向的第二个绑定函数,以此类推,直到找到与之相匹配的,然后就用apply函数改变调用函数的上下文,并且传入相关参数进行输出。