一起实现一个apply/call/bind函数吧!

75 阅读3分钟

本文从js代码的角度去实现apply/call/bind的主要功能

不会过多的考虑边界情况, 只是记录一下功能实现的大体思路

如果对你有所帮助或启发, 请帮忙点个赞哦~ Thanks♪(・ω・)ノ

Apply实现

传入两个参数, 第一个参数作为传入的this对象被绑定

第二个参数需要是数组或者类数组形式的数据

function foo(num1,num2){
  console.log('foo被执行...',this); 
  //foo被执行... String {'apply'}
  return num1+num2
}
​
//系统的apply方法
const result = foo.apply('apply',[4,5])
console.log('result',result); //result 9

根据apply方法的功能, 具体实现如下:

// 使用js模拟apply函数的实现
Function.prototype.moniApply = function(thisArg,args){
  //1.获取需要被执行的函数
  const fn = this //这里的this是根据默认绑定自动获取到的
  //2.处理边界情况 undefined和null
  if(thisArg===null || thisArg===undefined) thisArg = window
  //3.判断参数类型进行相关转化
  const argType = typeof(thisArg)
  if(argType!=='object'){
    // thisArg = Object(thisArg) //或者直接用Object包裹
    switch(argType){
      case 'number':
        thisArg = new Number(thisArg)
        break;
      case 'string':
        thisArg = new String(thisArg)
        break;
      case 'boolean':
        thisArg = new Boolean(thisArg)
        break;
      default:
        break;
    }
  }
  thisArg.fn = fn
  //4.处理参数个数的临界情况
  args = args || []
  const result = thisArg.fn(...args)
  delete thisArg.fn
  //5.返回结果
  return result
}

Call实现

根据MDN定义, call()方法使用一个指定到的this

和单独给出的一个或多个参数来调用一个函数

该方法接收的是一个参数列表

function foo(num1,num2){
  console.log('foo被执行...',this,num1,num2); 
  // foo被执行... String {'call'} 4 undefined
  return num1+num2
}
​
//系统的call方法
foo.call('call',4) // NaN

call方法和apply的功能类似, 只是第二个参数接收数据类型有差异

// 使用js模拟call函数的实现
Function.prototype.moniCall = function(thisArg,...args){
  //1.获取需要被执行的函数
  const fn = this
  //2.处理边界情况 undefined和null
  if(thisArg===null || thisArg===undefined) thisArg = window
  //3.判断参数类型进行相关转化
  const argType = typeof(thisArg)
  if(argType!=='object'){
    switch(argType){
      case 'number':
        thisArg = new Number(thisArg)
        break;
      case 'string':
        thisArg = new String(thisArg)
        break;
      case 'boolean':
        thisArg = new Boolean(thisArg)
        break;
      default:
        break;
    }
  }
  //4.调用需要被执行的函数
  thisArg.fn = fn
  thisArg.fn(...args)
  //5.最终结果返回
  const result = thisArg.fn(...args)
  if(result){
    delete thisArg.fn //尽量模拟真实call返回的格式
    return result
  }
}
​
function foo(num1,num2){
  console.log('foo被执行...',this,num1,num2);
  // foo被执行... String {'monicall', fn: ƒ} 1 2
  return num1+num2
}
​
const result = foo.moniCall('monicall',1,2,3)
console.log('result',result); //result 3

Bind实现

首先, bind()方法会返回一个新的函数

这个新函数的 this 被指定为 bind() 的第一个参数

而其余参数将作为新函数的参数,注意参数是可以累计传递

function sum(num1,num2,num3,num4,num5,num6){
  console.log('sum被执行',this,num1,num2,num3,num4,num5,num6);
}
​
const bind = sum.bind('bind',1,2) 
bind(3,4) //sum被执行 String {'bind'} 1 2 3 4

注意在实现bind函数的时候, 会返回一个新的函数

Function.prototype.moniBind = function(thisArg,...args){
  //1.获取到真实需要调用的函数
  const fn = this
  //2.处理边界情况 undefined和null
  if(thisArg===null || thisArg===undefined) thisArg = window
  //3.判断参数类型进行相关转化
  const argType = typeof(thisArg)
  if(argType!=='object'){
    switch(argType){
      case 'number':
        thisArg = new Number(thisArg)
        break;
      case 'string':
        thisArg = new String(thisArg)
        break;
      case 'boolean':
        thisArg = new Boolean(thisArg)
        break;
      default:
        break;
    }
  }
  //4.返回一个新的代理函数
  return function proxyFn(...proxyArgs){
    thisArg.fn = fn
    const finalArgs = [...args,...proxyArgs] //做参数累计传递的优化
    const result = thisArg.fn(...finalArgs)
    delete thisArg.fn
    return result
  }
}
​
const moniBind = sum.moniBind('moniBind',1,2)
moniBind(3,4,5) //sum被执行 String {'moniBind', fn: ƒ} 1 2 3 4 5 undefined