-->

js之重载

2020-12-02 07:20发布

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

标签: