call、apply、bind使用与内部实现

2,141 阅读2分钟

基本使用

call、apply、bind的核心功能都是改变函数的this指向,但彼此之间也有一些差别。

call

改变this指向且执行函数,额外参数以参数列表形式传入


let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

myFn.call(context,25,'180cm')  // 神秘的宝爷:25岁:180cm

apply

与call几乎一样改变this指向且执行函数,额外参数以数组形式传入


let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

myFn.call(context,[25,'180cm'])  // 神秘的宝爷:25岁:180cm

bind

改变this指向,的函数,而是返回一个this被改变的function,参数以参数列表形式传入


let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

let myFn2  = myFn.bind(context)

myFn2(25,'180cm') // 神秘的宝爷:25岁:180cm

手动实现call、apply、bind

要实现call、apply,我们需要做两件事,第一是获取到被绑定的函数,第二就是把被绑定的函数追加到劫持替换的对象上,然后再调用追加后的函数,由于js的this指向机制是指向调用者,所以这两步实现起来似乎比较容易

我们拿call来举例:


//context就是传入的替代this的对象
Function.prototype.myCall = function(context,...params){
    //当传入的替换变量不是对象的时候赋值成null
    if(typeof context === "object"){
        context = context || window
    }else{
        context = null
    }
    
    let funcName = Symbol() //使用Symbol类型定义临时方法名,避免context上的方法与临时方法重名
    context[funcName] = this //获取到调用的function
    context[funcName](...params)
    delete context[funcName]  //originFunction只是个临时属性,调用完毕后删除它
}

let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

myFn.myCall(context,25,'180cm')  // 神秘的宝爷:25岁:180cm


实现了call之后,apply就基本实现了,只是传参方式不一样:


//context就是传入的替代this的对象
Function.prototype.myApply = function(context,params){
    //当传入的替换变量不是对象的时候赋值成null
    if(typeof context === "object"){
        context = context || window
    }else{
        context = null
    }
    
    let funcName = Symbol() //使用Symbol类型定义临时方法名,避免context上的方法与临时方法重名
    context[funcName] = this //获取到调用的function
    context[funcName](...params)
    delete context[funcName]  //originFunction只是个临时属性,调用完毕后删除它
}

let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

myFn.myApply(context,[25,'180cm'])  // 神秘的宝爷:25岁:180cm


bind实现跟上两个不太一样,但是基于上面的实现也比较简单了,因为bind不调用函数,所以我们返回一个function内部执行myCall就行了


Function.prototype.myBind = function(context){
    return (...params)=>{
        this.myCall(context,...params)
    }
}

let context = {
    name:'神秘的宝爷'
}

const myFn = function(age,height){
    console.log(this.name+':'+age+':'+height)
}

const myFn2 = myFn.myBind(context)  

myFn2(25,'180cm')   // 神秘的宝爷:25岁:180cm