Skip to content

call、apply、bind

call、apply 详解

var a = {
    user:"gzh",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
b(); //undefined

结果是 undefined ?? 因为 函数中 this 永远指向其执行环境的上下文。所以 b = a.fn = function(){console.log(this.user)}; b() 此时的 this 就指向全局环境
那我们如何修改成我们想要的指向 a 对象

// 第一种 利用bind,因为bind是不会立即执行函数的,所有我们可以在申明的时候修改this的指向
var b = a.fn.bind(a);

//  call、apply 会立即执行函数  所以我们不能再申明的时候修改this,只能在调用的时候修改
b.call(a);
b.apply();
apply、call 的区别是什么??

上面说都可以利用后续参数进行传参。 apply 的传参 只能是一个且要为数组

b.apply(a,[1,2,3])

call 的后续参数就可以不一定为数组了,只能一个一个作为入参而已

a.call(a,1,2,3)   function(arg1,arg2,arg3){}   1,2,3

bind 详解

var a = {
   user:"gzh",
   fn:function(){
       console.log(this.user);
   }
}
var b = a.fn;

b.bind(a)   //什么都没有打印

我们发现没有立即打印 gzh 再次 b() //undefined

可见 bind 修改 this 的指向后 不会执行函数,而是生成一个新的函数

var c = b.bind(a);
c() //gzh

作用

1、异步函数绑定 this

js
var _this = this
var ev = element.addEventListener("click", function () {
  console.log(this) //此时this指向 元素element而不是当前的作用域
  //所以我们习惯  _this
})

var ev = element.addEventListener(
  "click",
  function () {
    console.log(this)
  }.bind(this)
) //在react中使用甚多

2、Math 函数的执行

js
console.log(Math.max(1, 2, 3, 2))

// Matn.max.call(Math,1,2,3,2);

3、修改 this 指向

我们如何修改函数中 this 的指向 这个我们就想到了 obj.fn()

源码

当我们自己去实现 call、apply、bind 的时候需要知道其核心的功能是那些,即上述所述的

  • 修改 this 的指向
  • 可以传入参数
  • 执行的时机不同

下面我们先按照上面的核心功能实现

js
Function.prototype.mCall = function (context) {
  // 如果没有传入第一个参数  那么this就指向window对象
  context = context || window
  context.fn = this
  context.fn()
}

这时候我们就想到几个问题。

  1. 后面的参数是作为函数的入参。
  2. 如果存在 fn 这个属性怎么办
  3. context 对象上多了 fn 属性

所以我们修改一下

js
Function.prototype.mCall = function (context) {
  // 如果没有传入第一个参数  那么this就指向window对象
  context = context || window
  // 1. 后面的参数是作为函数的入参
  const argus = [...arguments].slice(1)
  // 2. 如果存在fn这个属性怎么办  这个时候想到 Symbol 基本类型
  const fnName = Symbol("fn")
  context[fnName] = this

  context[fnName](argus)
  // 3. context对象上多了fn属性
  delete context[fnName]
}

apply 的实现

apply 与 call 的不同就是后面的入参是一个数组而不是多个参数

js
Function.prototype.mApply = function (context) {
  // 如果没有传入第一个参数  那么this就指向window对象
  context = context || window
  // 1. 后面的参数是作为函数的入参
  const argus = arguments[1]
  argus = Array.isArray(argus) ? argus : undefined
  // 2. 如果存在fn这个属性怎么办  这个时候想到 Symbol 基本类型
  const fnName = Symbol("fn")
  context[fnName] = this

  context[fnName](argus)
  // 3. context对象上多了fn属性
  delete context[fnName]
}

bind 的实现

call 这是返回的是一个修改函数的 this 的指向的函数,但是现在不会立即执行 这时候我们就需要想到闭包

支持柯里化传参

js
Function.prototype.bind = function (context) {
  // 如果没有传入第一个参数  那么this就指向window对象
  context = context || window
  // 2. 如果存在fn这个属性怎么办  这个时候想到 Symbol 基本类型
  let argus = [...arguments].slice(1)
  const fnName = Symbol("fn")
  context[fnName] = this

  return function () {
    let newArgus = [...arguments]
    context[fnName](...argus.concat(newArgus))
    // 3. context对象上多了fn属性
    delete context[fnName]
  }
}

call、apply、bind 的区别于作用

  1. call、apply、bind 都可以改变 this 的指向(指向第一个参数)。

  2. call、apply、bind 都可以利用后续参数进行传参(bind 很少用)。

  3. bind 是返回对应函数,以后调用;call、apply 是立即执行,返回值是函数的返回值。