龙空技术网

JavaScript怎么实现函数重载,琢磨了好些天终于明白了!

程序员小橙 395

前言:

今天兄弟们对“js中的add函数”大体比较着重,各位老铁们都需要了解一些“js中的add函数”的相关知识。那么小编在网络上网罗了一些关于“js中的add函数””的相关内容,希望兄弟们能喜欢,你们一起来学习一下吧!

前言

今天开发一个需求的时候,遇到了一个类似函数重载的场景。

这个需求可以接收一个下标。

当下标存在时,执行逻辑A。

当下标不存在时,执行逻辑B。

看着很简单对吧。

js复制代码if(index === undefined) {    A()} else {    B()}

直接一个if else解决,但是我们可以换种思路,通过函数重载的方式去实现它,后续如果参数格式继续变化,其拓展性也会更好。

因此就打算实现一个函数,通过js实现函数重载的能力。

知识铺垫

在实现函数重载之前,先回顾下什么是函数重载?

学过java的朋友当然很熟悉,但学习前端的同学可能就不太熟悉了,因为js并不支持函数重载。

什么是函数重载呢?

简单来说就是 同一个作用域内定义多个同名函数但参数个数或类型不同的情况。

针对不同的参数可以定义不同的函数逻辑,比如:

java复制代码public class OverloadingExample {    // 函数重载:整数相加    public int add(int a, int b) {        return a + b + 1;    }    // 函数重载:浮点数相加    public double add(double a, double b) {        return a + b;    }    public static void main(String[] args) {        OverloadingExample example = new OverloadingExample();        System.out.println("Sum of integers: " + example.add(2, 3));        System.out.println("Sum of doubles: " + example.add(2.5, 3.5));    }}

这里的add函数是存在同名的,但是二者类型不同,会去执行不同的代码。

那对于我们常用的js而言,函数重载是不支持的。

所以我们一般会采用条件判断或者对象参数这种方式实现:

js复制代码// 条件判断function exampleFunction(param1, param2 = null) {  if (param2 === null) {    // 处理只有一个参数的情况  } else {    // 处理两个参数的情况  }}
js复制代码// 参数对象function exampleFunction(options) {  if (object.keys(options).length == 1) {    // 处理只有一个参数的情况  } else {    // 处理多个参数的情况  }}
js复制代码// 函数名称区分function exampleFunctionOneParams(param) {  // 处理只有一个参数的情况}function exampleFunctionTwoParams(param1, param2) {  // 处理只有两个参数的情况}
代码思路

首先在上面的知识铺垫中,我们知道函数重载的核心是定义多个同名函数,通过不同的参数类型或组合执行不同的代码。

思路如下:

1.可以维护一个类,在类中️一个对象,对象中函数名做key,value是一个新的对象。

2.其中新的对象中 又以不同参数类型组合而成字符串作为key,而对应函数为value。

3.使用时先实例化class。

4.将目标函数和这个目标函数的参数集合传入,实例化对象中新增和目标函数同名的函数,并将目标函数和参数集合注册到映射对象中。

4.使用时,直接调用实例对象下的目标函数,由目标函数提供的同名函数去映射到对应的map中注册的真实函数并执行。

代码实现

js复制代码// 非法赋值标识let isAssignmentValid = falseclass Overload {  constructor() {    this.fnMap = new Proxy({}, {      set: setProxyValid    });    return new Proxy(this, {      set: setProxyValid    });  }  // 重载函数注册  reg(fn, arr = []) {    // 注册函数映射map    if(typeof fn !== 'function') throw new Error(`params err`);    isAssignmentValid = true    const typesKey = arr.join('_');    if (!this.fnMap[fn.name]) {      this.fnMap[fn.name] = new Proxy({}, {        set: setProxyValid      });    }    this.fnMap[fn.name][typesKey] = fn;    // 注册重载实例同名函数    if (!this[fn.name]) {      this[fn.name] = (...args) => {          const typesKey = getParameterTypesKey(...args);          const targetFn = this.fnMap[fn.name][typesKey];          return targetFn(...args);        }    }    isAssignmentValid = false  }}// proxy 禁止非法赋值 封装function setProxyValid(target, property, value) {  if(isAssignmentValid) {    target[property] = value    return true  } else {    throw new Error(`Cannot set attribute`);  }}// 获取参数类型keyfunction getParameterTypesKey(...args) {  const parameterTypes = args.map(arg => typeof arg);  const parameterTypesKey = parameterTypes.join('_');  return parameterTypesKey;}// 导出工厂函数export function OverloadFactory() {  return new OverloadJS()}
代码解释

这段代码是一个 JavaScript 的重载函数。

它允许你定义同名函数但参数不同的多个版本,然后根据传入的参数类型来自动调用对应的函数版本。

步骤一

代码先定义了一个 isAssignmentValid 标识,表示当前能否对重载实例或类进行赋值。

步骤二

代码的构造函数中会创建一个proxy对象 fnMap,这个对象的作用是存储函数和参数集合间的映射关系。

构造函数最后会返回一个指向this对proxy对象,后续的注册和函数调用都会在这个proxy实例上进行。

注:(代码中所有的proxy对象,会使用 setProxyValid 函数对set操作进行封装,禁止非法赋值,防止开发时开发人员误赋值,导致重载实例异常。)

步骤三

代码中的reg是重载函数的注册函数也是核心方法。

主要分成两部分

PART1:(注册函数映射map)

注册函数接受两个参数一个是需要重载的目标函数,一个是目标函数接受的参数类型数组。

首先,代码使用 typeof 运算符判断传入的 fn 是否为一个函数,如果不是,则抛出一个错误,提示参数错误。

接下来,将 isAssignmentValid 标志设置为 true ,表示当前处于合法赋值状态。这是为了在注册函数时允许对 fnMap 进行赋值操作。

然后,通过将参数数组 arr 使用 _ 连接起来,生成一个用于标识参数类型的 key,赋值给变量 typesKey 。

接着,通过检查 fnMap 中是否存在以 fn.name 为键的属性,来判断是否已经注册过该函数。如果不存在,则创建一个新的 Proxy 对象,并将其赋值给 fnMap 中以 fn.name 为键的属性。这个 Proxy 对象的作用是拦截对属性的赋值操作,以控制赋值的合法性。

最后,将函数 fn 存储到 fnMap[fn.name][typesKey] 的位置,以完成函数的注册和存储。

这段代码的作用是在注册函数时,将函数和对应的参数类型映射存储起来,以便后续根据传入的参数类型选择正确的函数版本进行调用。

PART2:(注册重载实例同名函数)

这段代码的作用是在注册函数时,为重载实例创建同名函数。

首先,代码判断当前实例对象中是否已经存在同名函数 fn.name 。如果不存在,则进入条件语句块。

在条件语句块中,代码定义了一个箭头函数,并将其赋值给实例对象的同名属性 this[fn.name] 。这个箭头函数接受任意数量的参数 ...args 。

在箭头函数内部,首先调用 getParameterTypesKey 函数,传入参数 ...args ,获取参数类型的 key,并将其赋值给变量 typesKey 。

接下来,通过访问 fnMap 属性,获取存储在 fnMap[fn.name][typesKey] 位置的目标函数,并将其赋值给变量 targetFn 。

最后,箭头函数调用 targetFn ,并传入参数 ...args ,返回函数调用的结果。 最后一行代码将 isAssignmentValid 标志设置为 false ,表示结束合法赋值状态。

这段代码的作用是为重载实例创建同名函数,这些同名函数会根据传入的参数类型选择对应的函数版本进行调用。这样,在调用重载函数时,可以直接通过实例对象的同名属性来调用对应的函数版本。

步骤四

最后导出工厂函数,提供业务侧使用。

基础使用创建重载实例

js复制代码const OverloadInstance = OverloadFactory() // 创建重载实例
注册同名函数
js复制代码OverloadInstance.reg(function exampleFn(param1, param2) { //注册同名函数    // 函数逻辑}, ['number', 'string'])OverloadInstance.reg(function exampleFn(param1) {    // 函数逻辑}, ['number'])
调用重载函数
js复制代码OverloadInstance.exampleFn(20, 'hello world') // 调用重载函数OverloadInstance.exampleFn(20)
代码用例简单用例:
js复制代码let x = OverloadFactory() // 创建重载实例x.reg(function name(a) { // 注册同名函数  console.log(a)}, ['number'])x.reg(function name(a, b) {  console.log(a + b)}, ['number', 'number'])x.reg(function text(a) {  console.log(a)}, ['string'])x.reg(function text(a, b) {  console.log(a + b)}, ['string', 'number'])x.name(20, 10) // 函数调用x.name(20)x.text('hello', 10)x.text('hello')
运行结果:代码缺陷

1.getParameterTypesKey 函数的类型判断太简单。

2.重载实例取空值异常处理缺失

总结

这段代码实现了一个重载函数库,它允许你定义同名函数但参数不同的多个版本,并根据传入的参数类型自动选择调用对应的函数版本。

但还不完美,待我后续优化。

标签: #js中的add函数 #js对象复制 不影响之前的对象吗 #js字符转int #js中的重载 #js函数重载是什么