高级前端程序员才会用到的-函数重载

194 阅读1分钟

函数重载

什么是函数重载?

所谓函数重载(method overloading) ,就是函数名称一样,但是输入输出不一样。或者说,允许某个函数有各种不同输入,根据不同的输入,返回不同的结果。凭直觉,函数重载可以通过if…else或者switch实现,这就不去管它了。但是if…else这种耦合性太高了

我们这个例子,是如果你不传入参数,就会输出所有的人,输入firstname,就会输出匹配的人,如果输入全名的人,也会输出匹配的人。如果用重载的话,用户体验确实会很好

 function method(obj,name,fnc){
      var old = obj[name];
      console.log(old instanceof Function);
      obj[name] = function(){
          console.log(arguments.length+" "+fnc.length);
          if(arguments.length === fnc.length){
              return fnc.apply(this,arguments);
          }else if(typeof old === "function"){
              return old.apply(this,arguments);
          }
      }
  }
  var people = {
      values:["Zhang san","Li si"]
  };
  method(people,"find",function(){
      console.log("无参数");
      return this.values;
  })
  method(people,"find",function(firstname){
      console.log("一个参数");
      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;
  })
  method(people,"find",function(firstname,lastname){
      console.log("两个参数");
      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());
  console.log(people.find("Zhang"));

这种方法巧妙的运用了闭包原理,既然js后面的函数会覆盖前面的同名函数,我就强行让所有的函数都留在内存里,等我需要的时候再去找它。有了这个想法,是不是就想到了闭包,函数外访问函数内的变量,从而使函数留在内存中不被删除。就是闭包。 实现过程:我们看一下上面这段代码,最重要的是method方法的定义:这个方法中最重要的一点就是这个old,这个old真的很巧妙。它的作用相当于一个指针,指向上一次被调用的method函数,这样说可能有点不太懂,我们根据代码来说,js的解析顺序从上到下为。

  1.解析method(先不管里面的东西)

  2.method(people,"find",function()  执行这句的时候,它就回去执行上面定义的方法,然后此时old的值为空,因为你还没有定义过这个函数,所以它此时是undefined,然后继续执行,这是我们才定义 obj[name] = function(),然后js解析的时候发现返回了fnc函数,更重要的是fnc函数里面还调用了method里面的变量,这不就是闭包了,因为fnc函数的实现是在调用时候才会去实现,所以js就想,这我执行完也不能删除啊,要不外面那个用啥,就留着吧先(此处用call函数改变了fnc函数内部的this指向)

  3.好了第一次method的使用结束了,开始了第二句,method(people,"find",function(firstname) 然后这次使用的时候,又要执行old = obj[name]此时的old是什么,是函数了,因为上一条语句定义过了,而且没有删除,那我这次的old实际上指向的是上次定义的方法,它起的作用好像一个指针,指向了上一次定义的 obj[name]。然后继续往下解析,又是闭包,还得留着。

  4.第三此的method调用开始了,同理old指向的是上次定义的 obj[name] 同样也还是闭包,还得留着。

  5.到这里,内存中实际上有三个 obj[name],因为三次method的内存都没有删除,这是不是实现了三个函数共存,同时还可以用old将它们联系起来是不是很巧妙

  6.我们 people.find() 的时候,就会最先调用最后一次调用method时定义的function,如果参数个数相同 也就是  arguments.length === fnc.length 那么就执行就好了,也不用找别的函数了,如果不相同的话,那就得用到old了  return old.apply(this,arguments); old指向的是上次method调用时定义的函数,所以我们就去上一次的找,如果找到了,继续执行 arguments.length === fnc.length  如果找不到,再次调用old 继续向上找,只要你定义过,肯定能找到的,对吧!

  总结:运用闭包的原理使三个函数共存于内存中,old相当于一个指针,指向上一次定义的function,每次调用的时候,决定是否需要寻找。

但是上面的方法还是有问题的,1 fnc这个对应的函数时不能传默认参数的,2 数据类型不同,还是会走同一个方法,下面我们做一个完整的全面的函数重载,这个一般只有做到高级程序员,并且开始做组件封装才会遇到这样的问题

function createOverload() {
const callMap = new Map();
function overload(...args) {
  const key = args.map((arg) => typeof arg).join(",");
  const fn = callMap.get(key);
  if (fn) {
    fn.apply(this, args);
  }
}
overload.addImpl = function (...args) {
  const fn = args.pop();
  if (typeof fn !== "function") {
    return;
  }
  const types = args;
  callMap.set(types.join(","), fn);
};
return overload;
}

const getUsers = createOverload();
getUsers.addImpl(() => {
console.log("!查询所有用户");
});
const searchPage = (page, size = 10) => console.log("按照页码和数量查询用户",page, size);

getUsers.addImpl("number", searchPage);
getUsers.addImpl("number", "number", searchPage);
getUsers.addImpl("string", (name) => console.log("按照姓名查询用户",name));
getUsers.addImpl("string", "string",(name, age) => console.log("按照性别查询用户",name,     age));
getUsers(1);
getUsers(1,20);
getUsers('zhang san');
getUsers('zhang san','li si');

image.png

我们通过new Map 根据不同的数据类型,用数据类型去定义这个key这样就做到了不同的key,对应不同的函数,实现我们想要的函数重载的效果。