美文网首页前端进阶之路让前端飞
JavaScript 函数重载的实现思路

JavaScript 函数重载的实现思路

作者: ac68882199a1 | 来源:发表于2018-03-04 19:37 被阅读47次

在之前一篇关于函数参数的文章中说到,javaScript 本身是没有函数重载这个概念的,出现同名函数会直接覆盖,不了解重载意义的小伙伴可以先看一下上一篇文章: JavaScript基础夯实——函数的参数

虽然本身不支持重载,但是由于可变参数的特性,我们就可以间接地实现函数的重载。

下面节介绍两种实现重载的思路:

参数个数实现方法

参数个数的方法主要是通过 arguments 关键字来实现的,比如:

arguments 的长度为 0 时,执行逻辑 a,为 1 时执行逻辑 b,为 2 时执行逻辑 c ……

function overrideByArgsLength (arg1, arg2, arg3) {
  const length = arguments.length
  if (length === 0) {
    console.log('logic a with no argument')
  } else if (length === 1) { // arg1 is exists
    console.log('logic b with argument arg1', arg1)
  } else if (length === 2) { // arg1 arg2 are exists
    console.log('logic c with arguments arg1 & arg2', arg1, arg2)
  } else { // all arguments are exists
    console.log('logic d with arguments arg1 & arg2 & arg3', arg1, arg2, arg3)
  }
}

你也可以在声明函数时不写参数,在函数体中直接通过下标的方式 arguments[0] arguments[1] 来调用参数。但是这样并不利于函数的可读性。

函数体中的判断语句也可以改写为 switch 语句

上面这种方式是 js 实现函数重载的一种非常简单的方式,来举个实际的例子看看:

某个群体有 10 个人,其中男性 6 人,女性 4 人,年龄分布在各个阶段:

const group = {
    total: 10,
    male: 6,
    female: 4,
    peoples: [{
      age: 12,
      sex: 'male'
    }, {
      age: 18,
      sex: 'female'
    }, {
      age: 22,
      sex: 'male'
    }, {
      age: 34,
      sex: 'male'
    }, {
      age: 60,
      sex: 'male'
    }, {
      age: 73,
      sex: 'female'
    }, {
      age: 6,
      sex: 'female'
    }, {
      age: 47,
      sex: 'male'
    }, {
      age: 3,
      sex: 'male'
    }, {
      age: 24,
      sex: 'female'
    }]
}

下面为这个群体添加一个查找方法,要求:

  1. 当不传入参数时,返回这个群体中的所有人及总人数
  2. 当传入一个参数时,如果这个参数能转化为 true 则返回群体中的所有男性及男性人数,否则返回所有女性及女性人数
  3. 当传入两个参数时(第二个参数为数字类型),如果第一个参数能转化为 true,则返回所有年龄大于等于第二个参数的人及人数,否则返回所有年龄小于第二个参数的人及人数

下面是通过判断语句简单实现的查找函数重载(无参数类型校验):

group.find = function (condition, age) {
    const length = arguments.length

  switch (length) {
    case 0:
      return {
        peoples: this.peoples,
        count: this.peoples.length
      }

    case 1:
      if (!!condition) {
        const male = this.peoples.filter(item => item.sex === 'male')
        return {
          peoples: male,
          count: male.length
        }
      } else {
        const female = this.peoples.filter(item => item.sex === 'female')
        return {
          peoples: female,
          count: female.length
        }
      }

    case 2:
      if (!!condition) {
        const elder = this.peoples.filter(item => item.age >= age)
        return {
          peoples: elder,
          count: elder.length
        }
      } else {
        const younger = this.peoples.filter(item => item.age < age)
        return {
          peoples: younger,
          count: younger.length
        }
      }
  }
}

group.find()
group.find(1)
group.find(0, 40)

这一大坨代码看这着实有些恶心,所有的逻辑都写在一个函数中,完全不利于代码的维护和复用,而当我们需要为不同的对象添加自定义方法时,总不能每添加一次就 switch 一遍吧?那有没有更高效便于复用的方式呢?

其实,通过闭包的方式,我们就可以相对方便地实现。

闭包方式实现方法

废话不多说,直接上代码:

function addMethodToObject (obj, name, fn) {
  const temp = obj[name]
  obj[name] = function () {
    if (fn.length === arguments.length) {
      return fn.apply(obj, arguments)
    } else if (typeof temp === 'function') {
      return temp.apply(obj, arguments)
    }
  }
}

上面代码中的方法是用来为一个对象添加自定义方法的方法,接收三个参数:

  1. 需要添加自定义方法的对象
  2. 自定义方法名
  3. 自定义方法的函数体

使用方式如下:

addMethodToObject(group, 'fetch', function () {
  return {
    peoples: this.peoples,
    count: this.peoples.length
  }
})

addMethodToObject(group, 'fetch', function (isMale) {
  if (!!isMale) {
    const male = this.peoples.filter(item => item.sex === 'male')
    return {
      peoples: male,
      count: male.length
    }
  } else {
    const female = this.peoples.filter(item => item.sex === 'female')
    return {
      peoples: female,
      count: female.length
    }
  }
})

addMethodToObject(group, 'fetch', function (elder, age) {
  if (!!elder) {
    const elder = this.peoples.filter(item => item.age >= age)
    return {
      peoples: elder,
      count: elder.length
    }
  } else {
    const younger = this.peoples.filter(item => item.age < age)
    return {
      peoples: younger,
      count: younger.length
    }
  }
})

group.fetch()
group.fetch(1)
group.fetch(0, 40)

下面说一下代码的执行逻辑:

每次执行 addMethodToObject 的时候,内部变量 temp 会持有 group 对象上的 fetch 属性,然后为 fetch 重新定义一个函数(闭包),由于闭包的特性,这个函数会持续持有 temp 变量和参数 fn

在闭包中,通过比较 fn.lengtharguments.length 的长度是否相同,如果相同的话,就执行当前闭包持有的 fn;如果不相同并且当前闭包持有的 temp 为函数,则执行 temp

可以发现,一共执行了三次 addMethodToObject,除了第一次的 tempundefined 以外,后面两次持有的 temp 都是上一次执行时生成的闭包。

而在我们调用自定义方法时,实际的执行逻辑和递归调用就有点类似了。感兴趣的小伙伴可以自己研究下到底是怎样的执行逻辑。

最后还要说一点:在上面的 demo 中,请不要使用 ES6 的箭头函数。由于箭头函数没有绑定自己的 this arguments 等,所以如果改写为箭头函数会报错。

扫码关注微信公众号【前端程序员的斜杠青年进化录】 微信扫码,给我赞赏一下~

相关文章

  • JavaScript 函数重载的实现思路

    在之前一篇关于函数参数的文章中说到,javaScript 本身是没有函数重载这个概念的,出现同名函数会直接覆盖,不...

  • 初识JavaScript函数Arguments模拟重载

    在 JavaScript 中并没有重载函数的功能,但每个函数中的 Arguments 对象可以模拟重载的实现。 1...

  • JavaScript 函数重载 浅析

    什么是函数重载? 首先想声明下,什么是函数重载,javascript中不存在函数重载的概念,(其实是个伪命题)但一...

  • JavaScript函数重载

    说明 JavaScript 中没有真正意义上的函数重载。 函数重载 函数名相同,函数的参数列表不同(包括参数个数和...

  • JavaScript Arguments与实现函数重载

    可以用函数内置的argument模拟函数重载 什么是arguments ? arguments 是一个类似数组的对...

  • JavaScript 函数重载

    概念 重载是指函数或者方法有相同的名称,但是参数个数或类型不相同的情形,这样的同名不同参的函数或者方法之间,互相称...

  • JavaScript函数重载

    转载自:作者Fundebug以及本文地址:https://blog.fundebug.com/2017/07/24...

  • JavaScript函数重载

    JQuery常用方法 $('.test') $('.test','td') $(['.test', 'td']) ...

  • JavaScript函数重载

    源代码 测试

  • c++ 运算符重载

    重载方法 运算符的重载实质上是函数的重载。函数的重载就是对一个已有函数赋予新的含义,使之实现新的功能。 重载运算符...

网友评论

    本文标题:JavaScript 函数重载的实现思路

    本文链接:https://www.haomeiwen.com/subject/vmfqfftx.html