JavaScript中的call、bind、apply方法

72 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

前言

如果我们对JavaScript中的this有了解,会不会有一些疑问,我们能不能改变this的指向,而不是由调用处的上层作用域决定。那么其实是有这样的方法的,就是js内部的call、bind、apply方法。

call方法

这个方法是函数对象Function上提供的一个方法,参数列表如下

 call(this: Function, thisArg: any, ...argArray: any[]): any;

我们先来使用一下

function foo(){
    console.log(this)
}
console.log('-------------------------');
const rookie = {
    name:'the rookie',
    pay:function(){
        console.log('pay: ', this);
    }
}
foo.call(rookie)

image.png

可以看到foo函数里this的指向已经变了,this指向的是传入的第一个参数。接着我们可以给方法加上一些参数

function foo(name,age){
    console.log(this,name,age)
}
console.log('-------------------------');
const rookie = {
    name:'the rookie',
    pay:function(){
        console.log('pay: ', this);
    }
}
foo.call(rookie,'shy',23)

image.png 那么也同样完成改变了this的效果,同时参数也可以传递过去,即相当于通过rookie对象调用了foo方法。

call方法源码实现


//源码实现
Function.prototype.myCall=function(target){
    let context = target || window;
    context.fn = this;//为对象添加方法(this指向调用myCall的函数)
    let args =[...arguments].slice(1);// 剩余的参数
    console.log('args: ', args);
    const result = context.fn(...args); //调用该方法,传递参数
    delete context.fn; //调用该方法,该方法this指向context
    return result;
}

这里我们使用foo.myCall(rookie,'shy',23)和前面达到同样的效果。

apply方法

这个方法和call方法类似,只是第二个参数变成了要传一个数组。

function foo(name,age){
    console.log(this,name,age)
}
console.log('-------------------------');
const rookie = {
    name:'the rookie',
    pay:function(){
        console.log('pay: ', this);
    }
}
foo.apply(rookie,['apply',23])

image.png

接下来看看其源码实现

Function.prototype.myApply=function(){
       //找出第一个参数,判断第一个参数是否为空,如果为空this还是指向window
          let target = arguments[0] || window
            //获取传递的参数
            var _arg  = arguments[1]
            target.fn = this; //为该对象添加方法,this指向调用myApply对象
            target.fn(..._arg);//调用方法
            //删除fn属性
            delete target.fn;                 
}

bind方法

这个方法稍微跟前面2个有点不太一样,bind()函数会创建一个新的绑定函数,这个绑定函数包装了原函数的对象。调用绑定函数通常会执行包装函数。

const bindNew=foo.bind(rookie)
bindNew('shy',23)

image.png

apply方法源码实现

Function.prototype.myBind = function(){
    const target = arguments[0] || window
    const fn = this // 调用bind的函数
    const args = [...arguments].slice(1) // myBind的参数
    const bind = function(){
        const args1 = [...arguments].slice() // bind的参数
        return fn.apply(target,args.concat(args1))
    }
    return bind
}

从源码也可以很好的理解,bind其实返回了一个新的函数,同样的执行

const bindNew=foo.myBind(rookie)
bindNew('bind方法实现w',23)

也是同样的效果,如果我们不传要绑定到的对象,则默认是window。

总结

从源码角度去理解这几个方法的使用,感觉更能深入其中,同样的让我们手写一个这样的方法改变this的指向,我们也可以按源码的思路去完成了。