每周分享:摸清 call、apply、bind

我们来看下面的一段代码

function Man (name) {
  this.name = name
}
let man = new Man('tom')
var name = 'window'
// 问候一下
let say = function (call, word) {
  console.log(`${call} ${this.name} ${word}`)
}
say('hello', 'welcome')
// hello window welcome

上面的代码很好理解,say() 的调用方是 window,所以 this.name 也就等于 window 下的变量 name 了。那么如果我想对 man 进行问候呢?我们可能会有下面的想法

man.say = say
man.say('hello', 'welcome')
// hello tom welcome

的确这样是可以达到目的的,但是 say 很可能就是一个公共方法,不属于任何其它对象,我只是想临时调用一下而已,修改原型链就显得有点不太合适了。那么又该怎么做呢?

其实说到底,我们最终要实现的目标是改变 say 方法调用的 this 指向,从 window 变成 man,这个时候就该请出我们今天的 3 个主角了:call()apply()bind(),这三个方法实际上就是函数原型链 Function.prototype 上的方法,每个函数都拥有的,主要用来改变函数的 this 指向。

call

先上代码

function Man (name) {
  this.name = name
}
let man = new Man('tom')
var name = 'window'
// 问候一下
let say = function (call, word) {
  console.log(`${call} ${this.name} ${word}`)
}
say.call(man, 'hello', 'welcome')
// hello tom welcome

call 的语法是

fun.call(thisArg, arg1, arg2, ...)

thisArg 表示 this 指向,如果为 nullundefined 则指向全局对象。后面的为参数列表。

apply

和 call 很类似,唯一不同的只是 apply 参数的传递不是一个一个,而是整个数组一次传入。

...
say.apply(man, ['hello', 'welcome'])
// hello tom welcome

语法

func.apply(thisArg, [argsArray])

注意这里的 argsArray 不光可以是数组,还可以是我们前面讲过的类数组对象

...
let param = {
    length: 2,
    0: 'hello',
    1: 'welcome'
}
say.apply(man, param)
// hello tom welcome

经常有同学记不清这俩之间的区别,我总结了一个记忆方法,call、call、call、call 有点类似射击,是一个一个,而 apply 相当于覆盖应用,一次性就传入了。

其实运用 ES6 展开符也可以很轻易地像 apply 一样使用 call

...
say.call(man, ...['hello', 'welcome'])
// hello tom welcome

bind

和 call 语法类似

fun.bind(thisArg[, arg1[, arg2[, ...]]])

但是作用却不一样

...
let greet = say.bind(man)
greet('hello', 'welcome')
// hello tom welcome

可以看到,不像 call 和 apply,bind 并非是立即执行的,而是会新生成一个新的方法,之后可随意调用。当然也可以提前将参数 bind 进去

...
let greet = say.bind(man, 'hello', 'welcome')
greet()
// hello tom welcome

或者

...
let greet = say.bind(man, 'hello')
greet('welcome')
// hello tom welcome

这三个方法在实际开发中是经常会用到的,一定要搞清楚三者的用法和区别。

注:箭头函数不绑定 this,所以使用上面的三个方法是无法绑定 this 的。

最新评论

发表评论

邮箱地址不会被公开。 必填项已用*标注