突击检查,你应该了解的js函数重载知识

✨什么是函数重载?

解释:

即同名函数根据参数类型/数量自动选择实现,JavaScript 本身不支持传统意义上的函数重载,但可以通过以下方法模拟类似功能:

直接上代码

js 复制代码
    function addMethods(obj, name, fn) {
        const old = obj[name];
        obj[name] = function (...args) {
          if (args.length === fn.length) {
            return fn.apply(this, args);
          } else if (typeof old === "function") {
            return old.apply(this, args);
          }
        };
      }
      const testObj = {};
      addMethods(testObj, "testFn", () => {
        console.log("没有参数");
      });
      addMethods(testObj, "testFn", (a) => {
        console.log("有一个参数", a);
      });
      addMethods(testObj, "testFn", (a, b) => {
        console.log("有2个参数", a, b);
      });

这里的流程是:

1.定义一个空对象testObj{};

2.定义一个为testObj添加函数的方法addMethods函数,参数分别为1个初始object对象、函数名称执行函数;

3.执行addMethods函数以及你想添加的函数重载执行方法;

现在可以在控制台执行testObj.testFn函数传入不同参数执行不同的逻辑

看一看效果:

「🧠分析」

addMethods设计了old一个变量名接受最新的函数,并且在后面根据调用的函数参数不同形成链式查找到对应函数执行

优点✅:简单几行代码实现js重载

缺点❌:不能根据参数类别执行对应方法,只能根据不同数量执行

⭐进阶方法

假如我们想根据同名函数、形参数量也相同,只是形参的类型可能不同而分别执行不同的逻辑,像这样:

js 复制代码
testFn(1,2,3)//打印number number number
testFn(1,'2','3')//打印number string string
testFn(1,2,3,false)//打印number number number boolean
...

仿照上面思路,创建一个函数,并且返回一个对象来调用其内部方法,暂时命名为createOverload把,然后调用的函数就命名为testFn

js 复制代码
export const createOverload = () => {
  const testFn=(...args:any[])=>{
  
  }
  ...
};

一步一步分析

1️⃣因为最终我们调用是以testFn(1,2,3)这种形式,那么testFn逻辑怎么确定呢?首先我们肯定能拿到参数的数量和类型,然后我们可以根据这个数量和类别组合生成1个唯一的key值来对执行对应的函数,既然需要有key=>value的形式那么自然而然想到map结构来保存key以及对应的function

js 复制代码
export const createOverload = () => {
  const map=new Map()
  const testFn=(...args:any[])=>{
  
  }
  const addMethods=(...args:any[])=>{
  
  }
};

2️⃣想要用map保存function,首先我们肯定得添加,创建一个addMethodsFn方法用于添加函数,核心逻辑就是提取其中的形参和需要执行的函数形成key=>function的映射

js 复制代码
export const createOverload = () => {
  const map = new Map();
  const addMethods = (...args: any[]) => {};
  const testFn= (...args:any[]) => {
    const mapKey = args.map((item) => typeof item).join(",");
    const fn = map.get(mapKey);
    if (!fn) {
      console.log("没有找到执行函数");
    } else {
      fn.apply(this, args);
    }
    };
};

3️⃣testFn的逻辑自然而然就是根据执行时传递的参数找到对应function并且执行了

js 复制代码
export const createOverload = () => {
  const map = new Map();
  const addMethods = (...args: any[]) => {
    const fn = args.pop();
    if (typeof fn !== "function") {
      console.log("没有传递执行函数");
    } else {
      map.set(args.join(","), fn);
    }
  };
  const testFn = (...args: any[]) => {
    const mapKey = args.map((item) => typeof item).join(",");
    const fn = map.get(mapKey);
    if (!fn) {
      console.log("没有找到执行函数");
    } else {
      fn.apply(this, args);
    }
  };
  testFn.addMethods = addMethods;
  return testFn;
};

测试一下

ts 复制代码
import { createOverload } from "@/utils/index";
  const a = createOverload();
  a.addMethods("number", "string", (a, b) => {
    console.log("number string", a, b);
  });
  a.addMethods("string", "string", (a, b) => {
    console.log("string string", a, b);
  });
  a.addMethods("number", "number", (a, b) => {
    console.log("number number", a, b);
  });
  a.addMethods("string", "number", "number", (a, b, c) => {
    console.log("number number number", a, b, c);
  });
  a.addMethods("boolean", "boolean", "number", (a, b, c) => {
    console.log("boolean boolean number", a, b, c);
  });
  a(1, 2, 3);
  a("1", "2");
  a(1, 2);
  a("1", 2, 3);
  a(true, false, 1);

依次打印

❤️至此就弥补了第一种方法的不足之处

⚔️闲谈之余

假如我们要给addMethods这个函数添加ts类别怎么添加呢?

还是一步一步来

1️⃣首先addMethods函数最后一个参数肯定是function,前面的参数是不固定的基本数据类型的字符串,定义一个AllType然后使用ts中关键字keyof索引查询来定义表示具体类型的typeKey

js 复制代码
type AllType = {
  boolean: boolean;
  string: string;
  number: number;
  symbol: symbol;
  bigint: bigint;
  null: null;
  undefined: undefined;
  object: object;
  function: Function
};

type typeKey = keyof AllType;
declare function AddImp(
  ...args: [...typeKey[], (...args:?[]) => void]
): void;

2️⃣前面不定量参数的类型确定了,那传入函数的参数类型是什么呢?可以确定的是传入函数的数量和类型肯定是与前面相关联的,像下面这样,即数量和类型是相关联的,那么就要想到使用ts中的泛型

js 复制代码
addMethods("string", "number", "number", (a:string, b:number, c:number) => {
    console.log("number number number", a, b, c);
  });
addMethods("number", "number", (a:number, b:number) => {
    console.log("number number", a, b);
  });
...  

修改AddImp

js 复制代码
type AllType = {
  boolean: boolean;
  string: string;
  number: number;
  symbol: symbol;
  bigint: bigint;
  null: null;
  undefined: undefined;
  object: object;
  function:Function
};

type typeKey = keyof AllType;
declare function AddImp<T extends typeKey[]>(
  ...args: [...T, (...args: ?[]) => void]
): void;

3️⃣具体如何关联起来呢?

举个栗子:

假如我前面的参数传了3个分别是字符串 stringnumbernumber,那么我后面函数参数a,b,c所传的实参类型 肯定就是要分别对应是stringnumbernumber,既然前面的字符串参数 为约定为了泛型 T,那么我就需要找到这个T所对应的原始类型

js 复制代码
  addMethods("string", "number", "number", (a:string, b:number, c:number) => {
    console.log("string number number", a, b, c);
  });

定义一个高级类型transFormKey,专门用于获取根据其传入的字符串参数获取其对应AllType中的原始类型

类型代码

js 复制代码
type AllType = {
  boolean: boolean;
  string: string;
  number: number;
  symbol: symbol;
  bigint: bigint;
  null: null;
  undefined: undefined;
  object: object;
  function:Function
};

type typeKey = keyof AllType;
type transFormKey<T extends typeKey[]> = {
  [P in keyof T]: AllType[T[P]];
};
declare function AddImp<T extends typeKey[]>(
  ...args: [...T, (...args: transFormKey<T>) => void]
): void;

将类型添加到之前的函数上再试试

js 复制代码
export const createOverload = () => {
  const map = new Map();
  const addMethods: typeof AddImp = (...args) => {
    const fn = args.pop();
    if (typeof fn !== "function") {
      console.log("没有传递执行函数");
    } else {
      map.set(args.join(","), fn);
    }
  };
  const testFn = (...args: any[]) => {
    const mapKey = args.map((item) => typeof item).join(",");
    const fn = map.get(mapKey);
    if (!fn) {
      console.log("没有找到执行函数");
    } else {
      fn.apply(this, args);
    }
  };
  testFn.addMethods = addMethods;
  return testFn;
};

添加完之后,在调用的时候就能看到类型提示了

并且如果函数的参数数目和前面的不一致时就会报类型错误

完结🎉

如果你觉得这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐、评论 💬

相关推荐
jinanwuhuaguo1 分钟前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
广州华水科技7 分钟前
深度测评2026年单北斗GNSS位移监测系统推荐,与高口碑变形监测设备一同引领行业新风尚
前端
Alice-YUE1 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
是上好佳佳佳呀2 小时前
【前端(十一)】JavaScript 语法基础笔记(多语言对比)
前端·javascript·笔记
莎士比亚的文学花园2 小时前
Linux驱动开发(3)——设备树
开发语言·javascript·ecmascript
CDN3603 小时前
排查实录:网站偶发502/504错误?360CDN回源超时配置与日志分析技巧
前端·数据库
之歆3 小时前
Day07_CSS盒子模型 · 样式继承 · 用户代理样式
前端·css
01漫游者3 小时前
JavaScript函数与对象增强知识
开发语言·javascript·ecmascript
DanCheOo3 小时前
AI 应用的安全架构:Prompt 注入、数据泄露、权限边界
前端·人工智能·prompt·安全架构
We་ct4 小时前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域