bind、call、apply 的用法


注:因为作用域的问题,不能用 let 声明 year

  1. var year = 2021
  2. function getDate(month,day){
  3. return this.year + '-' + month + '-' + day
  4. } //如果单独执行这个函数,this的指向是window
  5. let nextYear = {year: 2022}
  6. getDate.call(null,6,2) //"2021-6-2",this === null非严格模式下对应着 window
  7. getDate.call(this,6,2) //"2021-6-2"
  8. getDate.call(nextYear,6,2) //"2022-6-2",this === nextYear
  9. getDate.apply(nextYear,[6,2]) //"2022-6-2"
  10. getDate.bind(nextYear)(6,2) //"2022-6-2"

bind、call、apply 都是用来指定一个函数内部的 this 的值,三者的区别主要在于用法上,apply的第二个参数是一个数组,等价于call里面的第二个之后(包括第二个)的参数直接展开。
bind 的用法是当我们调用 getDate.bind() 的时候,它会去返回一个函数,这个函数的功能和 getDate 的功能是类似的,如果这个函数里面用到了this,那这个 this 就是我们绑定的 this。

代码


代码思路

  1. 将函数设为对象的属性
  2. 执行该函数
  3. 删除该函数

    call

    call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
    1. Function.prototype.call2 =
    2. function(context, ...args){
    3. context = (context === undefined ||
    4. context === null) ? window : context
    5. context.__fn = this
    6. let result = context.__fn(...args)
    7. delete context.__fn
    8. return result
    9. }
    当执行 getDate.call2(nextYear,6,2)的时候
    1. context = nextYear
    2. nextYear.__fn = this = getDate
    3. nextYear.__fn(...args) = nextYear.__fn(3,8)

    apply

    apply 和call 非常的类似,唯一的区别是 apply 的第二个参数是一个数组
    1. Function.prototype.apply2 =
    2. function(context, args){
    3. context = (context === undefined ||
    4. context === null) ? window : context
    5. context.__fn = this
    6. let result = context.__fn(...args)
    7. delete context.__fn
    8. return result
    9. }

    bind

    bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
    1. Function.prototype.bind2 =
    2. function(context, ...args1){
    3. context = (context === undefined ||
    4. context === null) ? window : context
    5. let _this = this
    6. return function(...args2){
    7. context.__fn = _this
    8. let result = context.__fn(...[...args1,...args2])
    9. delete context.__fn
    10. return result
    11. }
    12. }
    context.__fn =_this 等同于给原来的对象新增了一个临时的属性。如果我们的对象做了一些配置使对象不能新增属性,那我们的bind2就有问题了。其实,我们不只可以在 context 上去增加属性,我们可以把目光看向更底层,比如说在 Object.prototype 上面去舔加属性。

比如说如果我们的obj上面不允许有fn,那我们也可以在 Object.prototype 上面添加一个 fn2

  1. let obj = {a:1,fn:function(){
  2. console.log(this.a)
  3. }}
  4. obj.fn() // 1
  5. Object.prototype.fn2 = function(){
  6. console.log(this.a)
  7. }
  8. obj.fn2() //1