如何理解 Call, Apply, Bind?

今天,介绍这两个方法的区别以及一些妙用,还有用法相似的 bind 的方法。

它们最主要的作用,是改变 this 的指向。在平时的工作中,除了在写一些基础类,或者公用库方法的时候会用到它们,其他时候应用场景并不多( React 我习惯用箭头函数)。

基本介绍

func.call(thisArg, arg1, arg2, ...)
func.apply(thisArg, [argsArray])
func.bind(thisArg[, arg1[, arg2[, ...]]])

简单来说,他们都是改变函数执行时的上下文,参数形式都是差不多的。

先来看 call()apply()

两个方法,乍一看没啥区别,除了参数部分,也都是于JavaScript 1.3 中实现。

var test = {
  color: '红色',
  say: function(){
    console.log("颜色是"+this.color)
  }
}
test.say()
// 颜色是红色

var test2 = {
  color:"黄色"
}
test.say.call(test2) // 颜色是黄色
test.say.apply(test2) // 颜色是黄色

这样看来确实很容易搞混,具体的区别看以下例子🌰

var test = {
  color:'红色',
  say:function(n,graph){
    console.log(`${n}${this.color}${graph}`)
  }
}

test.say.call({color:'绿色'}, 10, '矩形') // 10个绿色的矩形
test.say.apply({color:'蓝色'}, 2, '三角形') // Uncaught TypeError: CreateListFromArrayLike called on non-object
test.say.apply({color:'蓝色'}, [2, '三角形']) // 2个蓝色的三角形

昂,差别就是这个了,传入的参数形式不一样,因为现在有了展开符 来 传入/接受 参数,所以差异已经不大了,
但是在早期的环境中,如果不确定参数的话,一般都会选择用 apply 来一次性传入,并且使用 arguments 来处理。

说完了 applycall ,再来说说 bind

bind 方法与 applycall 很相似,也是可以改变函数体内 this 的指向。

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

还是举个例子来说吧:

var test = {
  color: '红色',
  say: function(){
    console.log("颜色是"+this.color)
  }
}
test.say()
// 颜色是红色

var test2 = {
  color:"黄色"
}
test.say.call(test2) // 颜色是黄色
test.say.apply(test2) // 颜色是黄色
test.say.bind(test2) // f() {..} 

// 可以看到bind()并没有输出,需要自执行或者赋值给一个变量然后调用
test.say.bind(test2)() // 颜色是黄色
var bindTest2 = test.say.bind(test2)
bindTest2() // 颜色是黄色

简单总结来说

  • call()/apply()/bind() 都可以改变函数内的 this 指向;
  • call()/apply()会立即执行函数,而bind() 不会立即执行;
  • bind() 会返回一个新得函数,并不会马上执行;
  • apply(),传入的参数需要数组格式;
    • fun.apply(thisArg, [arg1, arg2, ...])fun.call(thisArg, arg1, arg2, ...)
  • bind() 传入的参数的方式和 call() 一样。
    • fun.call(thisArg, arg1, arg2, ...)fun.bind(thisArg, arg1, arg2, ...)

疑惑部分

1.bind() 传入的参数部分是否可以在 bind 的时候不传入,后续使用的时候在传入?

可以

function add(a, b) {
  return a + b;
}
var add1 = add.bind(null);
console.log(add1(1,3));  // 4 

var add2 = add.bind(null,10)
console.log(add2(5));  // 15

参考

Function.prototype.call() - MDN
Function.prototype.apply() - MDN
Function.prototype.bind() - MDN

OBKoro1 - js 面试官想了解你有多理解 call,apply,bind?
Micherwa - 「干货」细说 call、apply 以及 bind 的区别和用法
公子 - 如何理解,javascript bind
Schaos - 一次搞懂前端面試最愛問的 apply、bind、call