js之重载
在面向对象语言中, 重载就是一组具有相同名字、不同参数列表的函数,但在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函数改变调用函数的上下文,并且传入相关参数进行输出。