引言
最近在 js 的学习中, 看到了函数重载的问题, 一开始, 只看到了实现代码, 看着代码冥思苦想了半个小时, 总算是理清了其实现的原理, 也为其实现的巧妙感到赞叹, 也是在自己搞懂原理之后, 去网络上搜索了下, 才知道, 这个实现方法是 jQuery 作者 John Resig 在JavaScript 忍者秘籍中对函数重载的实现, 设计十分的巧妙, 写下此文, 给大家做一个分享
什么是函数的重载
重载, 简单说, 就是函数或者方法有相同的名称, 但是参数列表不相同的情形, 这样的同名不同参数的函数或者方法之间, 互相称之为重载函数或者方法.
先来看看效果
- function addMethod (obj, name, fn) {
- var old = obj[name];
- obj[name] = function () {
- if (fn.length === arguments.length) {
- return fn.apply(this, arguments)
- } else if (typeof old === 'function') {
- return old.apply(this, arguments)
- }
- }
- }
- var person = {userName: 'bear 鲍的小小熊'}
- addMethod(person, 'show', function () {
- console.log(this.userName + '---->' + 'show1')
- })
- addMethod(person, 'show', function (str) {
- console.log(this.userName + '---->' + str)
- })
- addMethod(person, 'show', function (a, b) {
- console.log(this.userName + '---->' + (a + b))
- })
- person.show()
- person.show('bkl')
- person.show(10, 20)
输出的结果
- //bear 鲍的小小熊 ---->show1
- //bear 鲍的小小熊 ---->bkl
- //bear 鲍的小小熊 ---->30
我们给一个对象添加了一个 show 方法, 这个 show 方法, 每次传入的参数不一样, 它进行的处理也是不一样的
为了后文更好理解, 这里先对 fn.length 这个大家可能陌生的属性做个解释, 先看下面的代码
- function fn(a,b,c) {}
- fn.length // 3
- function fn(a,b,c,d) {}
- fn.length // 4
fn.legnth, 是函数 fn 在定义时, 形参的个数. 好了, 让我们继续往下讲吧
这个 addMethod 函数, 简单的来说, 就是给一个对象添加一个指定 name 的方法 fn(后文中为了方便大家理解, 我们就以这个例子中的 show 来指代这个 name 吧), 他利用了闭包, 通过变量 old, 将每次传进来的 fn 给保存起来, 我们每次调用这个 show 方法, 根据传入的参数的不同, 我们的代码可能多次通过 old 来找到之前传入的 fn 函数
下面我们来对这个方法进行解析为了看的更加直观, 我们对之前的 addMethod 的函数做一点小小的改造, 其实就是加入了一个 console.log(), 可以方便我们理解, 函数的执行过程
- function addMethod (obj, name, fn) {
- var old = obj[name];
- obj[name] = function () {
- console.log(1) // 打印 1
- if(fn.length === arguments.length){
- console.log(2) // 打印 2
- return fn.apply(this,arguments);
- }else if(typeof old === 'function'){
- console.log(3) // 打印 3
- return old.apply(this,arguments);
- }
- }
- }
- addMethod(person, 'show', function () {
- console.log(this.userName + '---->' + 'show1')
- })
- addMethod(person, 'show', function (str) {
- console.log(this.userName + '---->' + str)
- })
- addMethod(person, 'show', function (a, b) {
- console.log(this.userName + '---->' + (a + b))
- })
下面, 我们看一下 person.show 方法, 在不传参, 传了一个参数, 与传了两个参数时, 函数执行的具体过程,
- person.show(10, 20)
- 1
- 2
bear 鲍的小小熊 ---->30
可见, 传入两个参数的时候, 只打印了一个 1, 一个 2, 就将对应的执行函数执行了. 其实这个时候 person.show 函数的作用域内 fn 为下面这个函数
- function (a, b) {
- console.log(this.userName + '---->' + (a + b))
- }
- person.show('bkl')
- 1
- 3
- 1
- 2
bear 鲍的小小熊 ---->bkl
传入一个参数的时候执行结果为 1 --> 3 --> 1 --> 2 --> 处理后的结果 在这个过程中由于执行 person.show 方法时, fn.length === 2, 而我们传入的参数为 1 个, 那么函数会执行到
return old.apply(this,arguments);
这个时候的 old 是什么呢? 这个时候的 old 其实是在下面这个函数执行之前的 person.show 方法
- addMethod(person, 'show', function (a, b) {
- console.log(this.userName + '---->' + (a + b))
- })
执行之后 person.show 函数作用域内的 fn 函数也就是下面这个方法
- function (str) {
- console.log(this.userName + '---->' + str)
- }
- person.show()
- 1
- 3
- 1
- 3
- 1
- 2
bear 鲍的小小熊 ---->show1
上面的也是同理, 根据这个输出结果, 不难看出, 当没有传递参数时, 通过闭包的 old 变量, 我们可以一路向上找到这个方法.
- function () {
- console.log(this.userName + '---->' + 'show1')
- }
结语
就这样, 我们通过闭包中的 old 变量, 将对不传参数, 传了一个参数和传了两个参数进行区别处理的方法给串联了起来. 实现了 js 的重载. 再次感叹一下, 这个方法真的很巧妙. 真是漂亮又充满魅力的代码
如果觉得还可以, 请点赞鼓励一下, 谢谢!
来源: https://juejin.im/post/5b2529dde51d4558aa04e683