Featured image of post this、call、apply 和 bind

this、call、apply 和 bind

什么是 this

在说 callapplybind 之前,我们先来看看 Javascript 中的 this 是什么。

先来看一段代码 ⬇️ ⬇️ ⬇️

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var a = 1
const obj = {
    a: 2,
    fn: function() {
        console.log(this.a)
    }
}

const b = obj.fn
obj.fn() // 2
b() // 1

上面代码中,虽然 obj.fnb 指向同一个函数,但是执行结果不一样。这种差异的原因,就在于函数内使用了 this 关键字。 this 指的是函数运行时所在的环境。

在内存图中,变量 obj 的值指向了一个地址,这个地址里保存了 afn ,而由于 fn 是一个函数,那么 fn 的值也是一个地址,在 fn 指向这个地址中保存了 fn 这个函数的函数体,所以这个函数是一个单独的值,那么他就可以在不同的环境中执行。

obj的内存图

this 就指的是函数运行时的环境。

  • 所以当运行 obj.fn() 时,fn() 的运行环境是 obj,所以这时候 this.a 就是 obj.a
  • 当运行 b() 时,b() 的运行环境是 window,所以这时候 this.a 就是 window.a

由此得出,this 的值就是 . 前面的环境。

call、apply 和 bind

了解了 this 之后,就可以说说 callapplybind 了。

call

MDN对call的解释 ->

MDN: call() 允许为不同的对象分配和调用属于一个对象的函数/方法。 call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

通俗点讲就是,call 可以改变调用函数时的 this 值。以便我们可以在其他的地方调用同一个方法而不用再写一次该方法。

了解了概念,那就看看怎么用吧。还是上面的代码,我们试着用 call 来调用它。

1
2
3
4
5
//当我这样写的时候,得到的结果还是 2
obj.fn.call(obj)  //2

//但是,当我这样写的时候,结果就和上面不同了
b.call(obj)  //2

在调用 b() 的时候,使用 call 改变了 this 的指向,让他仍然指向 obj ,于是运行 b() 的结果没我们就得到了和 obj.fn() 一样的结果了。


总结一下 call 的用法:

1
obj.fn.call(this, arg1, arg2)

call 的第一个参数是你要指定的 this,之后的参数就是函数自身的参数。

apply

MDN对apply的解释 ->

call() 方法的作用和 apply() 方法类似,区别就是 call() 方法接受的是参数列表,而 apply() 方法接受的是一个参数数组。

直接来看用法吧:

1
obj.fn.apply(this, [arg1, arg2])

apply 的第一个参数是你要指定的 this,之后的数组就是函数自身的参数组成的数组。

bind

MDN对bind的解释 ->

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

bind() 返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

还是上面的代码:

1
2
3
4
5
let c = obj.fn.bind(obj)

c()  //2
c.call(null)  //2
c.call(obj)  //2

我们让 c 等于 fn.bind(obj) 之后我们发现,使用 call 指定 c()this 为任意对象,都不会改变运行结果。这就是因为bind() 返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

他返回的是一个新函数, this 是创建时候指定的 this 值,并且不会被 call apply 等改变。

callapply 不同的是,上面两个在指定了 this 之后立即执行,而 bind 返回的是一个新的函数,在改变了函数的 this 的同时不会执行函数,可以在之后需要的时候再调用。

以上就是 this 、call 、 apply 和 bind 的简单讲解了。