JavaScript实现函数重载的代码示例

作者:拾光

时间:2026-10-30

浏览:128

本文详细讲解如何在JavaScript中实现函数重载。通过Proxy对象拦截赋值操作,构建参数类型与函数的映射关系,从而支持同名不同参的函数调用。文章提供了完整的工厂模式代码、注册调用示例及潜在缺陷分析,帮助开发者拓展JS函数的灵活性。

文章目录

前言

在近期的开发工作中,我遇到了一个类似函数重载的实际场景。

该需求允许接收一个可选的下标参数。

具体逻辑为:若下标存在,则执行逻辑A;若下标缺失,则执行逻辑B。

乍看之下,这似乎是一个简单的任务。

if(index === undefined) {
    A()
} else {
    B()
}

通常可以直接使用if else语句来处理,但为了提升代码的可扩展性,特别是当未来参数格式发生变化时,我们可以尝试通过函数重载的思路来实现。

基于此,我决定研究并实现一个利用JavaScript特性来模拟函数重载功能的函数库。

知识铺垫

在动手实现之前,有必要回顾一下函数重载的概念。熟悉Java的开发者对此应不陌生,但对于前端开发人员而言,由于js并不支持函数重载,这一概念可能较为陌生。

那么,究竟什么是函数重载?

简而言之,同一个作用域内定义多个同名函数但参数个数或类型不同的情况。。例如:

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函数虽然同名,但根据参数类型的不同,会执行不同的代码逻辑。

然而,在常用的JavaScript语言中,原生并不直接支持函数重载机制。

因此,我们通常采用条件判断或对象参数传递的方式来模拟这一行为:

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

代码思路

基于前文的分析,函数重载的核心在于定义多个同名函数,并根据不同的参数类型或组合来执行相应的代码逻辑。

具体的实现思路如下:

1. 维护一个类,该类内部包含一个对象,该对象的键为函数名,值为另一个新对象。2. 这个新对象以不同参数类型组合生成的字符串作为键,对应的函数作为值。3. 使用时需先实例化该类。4. 将目标函数和这个目标函数的参数集合传入,通过实例化对象中新增和目标函数同名的函数,并将目标函数和参数集合注册到映射对象中。5. 调用时,直接调用实例对象下的目标函数,该函数会映射到注册的真实函数并执行。

代码实现

// 非法赋值标识
let isAssignmentValid = false
class 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`);
  }
}
// 获取参数类型key
function 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对象,会使用 函数对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,表示结束合法赋值状态。

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

步骤四

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

基础使用

创建重载实例

const OverloadInstance = OverloadFactory() // 创建重载实例

注册同名函数

OverloadInstance.reg(function exampleFn(param1, param2) { //注册同名函数
    // 函数逻辑
}, ['number', 'string'])
OverloadInstance.reg(function exampleFn(param1) {
    // 函数逻辑
}, ['number'])

调用重载函数

OverloadInstance.exampleFn(20, 'hello world') // 调用重载函数
OverloadInstance.exampleFn(20)

代码用例

简单用例:

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. 重载实例取空值时的异常处理缺失。

总结

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

以上就是JavaScript实现函数重载的代码示例的详细内容,更多关于JavaScript实现函数重载的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章