手写apply()、call()、bind()的源码实现

call()

call()方法用来改变函数的this指向,它接收多个参数,第一个参数为执行作用域,第二个及以后的参数是传递给函数的参数

call方法定义到函数原型对象上,以便所有的函数使用:

// 全局添加一个_call方法
Function.prototype._call = function(obj) {
    
}

它接收一个参数obj,这个obj就是函数的执行作用域。如果_call()方法没有传递任何参数,则默认执行作用域为window对象

// 全局添加一个_call方法
Function.prototype._call = function(obj) {
    // 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
    var _obj = obj ? Object(obj) : window //新增
}

让函数的执行作用域为参数obj

// 全局添加一个_call方法
Function.prototype._call = function(obj) {
    // 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
    var _obj = obj ? Object(obj) : window
    _obj.fn = this //新增
}

获取其他函数参数,可以采用ES6的数组方法遍历arguments,最后,要把obj对象中的fn方法移除

// 全局添加一个$call方法
Function.prototype._call = function (obj) {
  // 参数是否存在,如果存在则转为Object类型,否则直接取window对象为默认对象
  var _obj = obj ? Object(obj) : window
  // 把调用$call()方法的那个函数保存在目标对象中
  _obj.fn = this

  // 保存参数的数组
  var argArr = Array.from(arguments).slice(1) //新增

  // 执行obj的fn方法,把arg拆分
  _obj.fn(...argArr) //新增

  // 执行完之后删除这个方法
  delete _obj.fn //新增
}

让我们来测试一下

  <script>
    window.c = 1314
    function add(a,b){
      console.log(this);
      return a+b+this.c
    }
    let obj = {
      c:520
    }
    console.log('-------1',add._call(obj,10,20));
    console.log('-------2',add._call(null,10,20));
  </script>

apply()

有了上面_call()的实现,那么_apply()就很好编码了

Function.prototype._apply = function(obj, argArr) {
    // 如果obj不存在则默认window对象
    var _obj = obj ? obj : window
    // 给_obj添加fn方法
    _obj.fn = this
    // 获取第二个数组参数
    var args  = arguments[1]

    // 当这个参数数组不存在或者为空时,直接执行函数,否则把数组扩展运算后传递给函数并执行
    if (!args || args.length == 0) {
        _obj.fn()
    } else {
        // 执行obj的fn方法,把arg拆分
        _obj.fn(...args)
    }
    // 移除这个方法
    delete _obj.fn
}

测试

结果也是没问题的 

bind()

bind()依然用来改变函数this指向,但是它不会像call()与apply()方法那样立即执行这个函数,而是返回一个新的函数给外部,外部用一个变量去接收这个新函数并执行。新函数内部的this指向方法的第一个函数,后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入。之后执行新函数相当于执行了目标函数。        

bind()方法返回的那个函数不仅仅可以作为普通函数调用,还可以作为一个构造函数被调用

bind()方法的参数具有一个特性,就是函数柯里化,简单来说就是保留一个参数的位置,再第二次传参的时候自动把参数存入到这个位置中

Function.prototype._bind = function (obj) {
  // 如果obj不存在则默认window对象
  var _obj = obj ? obj : window
  // 截取传给函数的参数
  var args = Array.from(arguments).slice(1)  //新增
  // 保存这个函数,以便后续使用
  var _fn = this  //新增

  // 创建一个待会儿返回出去的函数,这个函数会赋到外部变量中
  var bindFn = function () {
    // _bind方法会返回一个函数,这里获取_bind方法返回的函数的参数保存起来
    var newArgs = Array.prototype.slice.call(arguments)

      // 通过call去改变this指向,使新函数内部的this指向方法的第一个函数
      // 后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入,实现函数柯里化
      return _fn.call(_obj, ...args, ...newArgs) //新增


  }
//返回一个新的函数给外部
  return bindFn //新增  
}

bind()返回的函数可以作为构造函数,那么它得继承调用它的那个函数的原型对象以及属性,这里创建一个媒介函数,用来实现寄生组合式继承:

Function.prototype._bind = function (obj) {
  // 如果obj不存在则默认window对象
  var _obj = obj ? obj : window
  // 截取传给函数的参数
  var args = Array.from(arguments).slice(1)
  // 保存这个函数,以便后续使用
  var _fn = this

  // 创建一个待会儿返回出去的函数,这个函数会赋到外部变量中
  var bindFn = function () {
    // 获取_bind方法会返回一个函数,这里获取_bind方法返回的函数的参数
    var newArgs = Array.prototype.slice.call(arguments)

      // 通过call去改变this指向,使新函数内部的this指向方法的第一个函数
     // 后面逐个列举的任意个数参数将作为目标函数的参数一一对应传入,实现函数柯里化
      return _fn.call(_obj, ...args, ...newArgs)
    

  }
  // 创建一个中介函数,以便实现原型继承
  var ProtoFn = function () { }   //新增
  ProtoFn.prototype = _fn.prototype   //新增
  bindFn.prototype = new ProtoFn()   //新增
  // 返回bindFn的函数给外部
  return bindFn   //新增
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值