call、apply、bind的实现

Posted by csy on 2018-11-20

call、apply、bind

  • 都用来改变函数体内部的this指向
  • 第一个参数都是this,如果没有第一个参数,this 的值绑定为全局对象
  • 都可以利用后续参数传参
    • call:fn.call(this,1,2,3)
    • apply:fn.apply(this,[1,2,3])
    • bind:fn.bind(this)(1,2,3)

call的实现

思路: 1) 将函数设为对象的属性 2) 执行该函数 3) 删除该函数

1
2
3
4
5
6
Function.prototype.myCall = function (context = window, ...rest) {
  context.fn = this
  let result = context.fn(...rest)
  delete context.fn
  return result
}

apply的实现

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myApply = function (context = window, params=[]) {
  context.fn = this
  let result
  if(params.length) {
    result = context.fn(...params)
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

bind的实现

  • 八卦一下
    this的绑定规则:默认绑定、隐式绑定、显示绑定、new绑定,为了解决隐式丢失问题(被隐式绑定的函数会丢失绑定对象),有一种强制绑定的用法,即创建一个包裹函数,传入所有的参数,并返回接收到的所有值。由于是非常常用的模式,所以ES5提供了内置的方法 Function.prototype.bind

  • 思路:
    1) 返回一个函数 2) 可以传入参数 3) bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.myBind = function (context, ...rest) {
    if (typeof this !== 'function') {
        throw new TypeError('invalid invoked!')
    }
    var self = this
    function F(...args) {
        if (this instanceof F) { //当作为构造函数时,this 指向实例,此时结果为 true
            return new self(...rest, ...args)
        }
        return self.apply(context, rest.concat(args))
    }
    delete F.prototype
    return F
}