call,apply,bind的内部原理实现

call

call 方法使用一个函数执行的时候更改本身 this 指向,并传入一个或者多个参数。

var obj = {
  name: '$call'
}
function _fun() {
  console.log(this.name, ...arguments)
}

_fun.call(obj, 'call1', 'call2', 'call3') // $call call1 call2 call3

内部实现原理:

Function.prototype.$$call = function (context) {
  // 第一个参数为 this 指向值,如无则指向 window
  context = context || window
  // 将本身的函数保存下来,在后面需要执行,这一步 this 的指向已经指向了 context 
  context.fn = this
  // 将后面传入的参数转为数组,取除第一个 this 指向剩下的所有参数
  let args = [...arguments].slice(1)
  // 执行函数本身,并将参数传入
  let result = context.fn(...args)
  // 销毁函数,避免作用域污染
  delete context.fn
  return result
}

apply

apply 方法同 call 一样使用一个函数执行的时候更改本身 this 指向,只是传参的时候只有一个,并且必须是数组(如果call与apply传参类型记不清,可以根据方法的第一个字母来区分,apply -> a(首字母) -> array)。

var obj = {
  name: '$call'
}
function _fun() {
  console.log(this.name, ...arguments)
}

_fun.apply(obj, ['call1', 'call2', 'call3']) // $call call1 call2 call3

内部实现原理:

Function.prototype.$$apply = function (context) {
  // 第一个参数为 this 指向值,如无则指向 window
  context = context || window
  // 将本身的函数保存下来,在后面需要执行,这一步 this 的指向已经指向了 context 
  context.fn = this
  // 将后面传入的参数转为数组,取除第二个参数
  let args = [...arguments][1]
  // 如果第二个参数不是对象则报错
  if (typeof args !== 'object') {
    throw Error('CreateListFromArrayLike called on non-object')
    return
  }
  // 执行函数本身,并将参数传入
  let result = context.fn(...args)
  // 销毁函数,避免作用域污染
  delete context.fn
  return result
}

bind

bind 方法与 call 和 apply 不同的点是后续的参数没有要求,但是 bind 会返回一个 this 指向已改变的函数,相同的是第一个参数就是 this 指向值

var obj = {
  name: '$call'
}
function _fun() {
  console.log(this.name, ...arguments)
}

_fun.call(obj, ['call1'], 'call3')('call2') // $call ["call1"] call3 call2

内部实现原理:

Function.prototype.$$bind = function (context) {
  // 取bind执行的时候除第一个后续的所有参数
  let args = [...arguments].slice(1)
  // 函数本身缓存
  let _this = this
  // 返回函数
  return function () {
    // 合并返回函数执行传入的参数
    let bindArg = [...args, ...arguments]
    // 再次调用时的函数本身执行
    return _this.call(context, ...bindArg)
  }
}

 

posted @ 2020-07-02 16:00  前端杂货  阅读(660)  评论(1编辑  收藏  举报