手写 apply, call, bind

71 阅读2分钟

apply

改变函数的 this 执向, 这个方法是在对象的原型链上的, 调用会执行目标函数, 第一个参数是需要指向的 this, 第二参数是一个数组, 是函数的参数

function myApply (context, ...args) {
    if (typeof context !== 'function') {
        throw new Error('type error')
    }
    // 要执行函数, 所以要记录函数本身
    const self = this // 这个 this 执向调用者
    
   /** 这里有个知识点, 普通函数的调用者是谁, 他的 this 就执向谁, 所以我们可以将传进来的 this 
      修改下
   **/
   context = context === null || context === undefined ? globalThis : Object(context)
   // 这样我们将传进来的这个 this 包装成一个对象 
   
    // 执行函数
    // 执行前我们需要将当前的函数执行, 并将当前函数中的 this 指向传入的 this
    const key = Symbol()
    
    /*
       这里的原理: 
       const context = {
           key: function (arg) {
               console.log(this)
           }
       }
       context.key(arg) // context
       看到这里你应该明白为什么箭头函数不能使用这些方法了吧
    */
    context[key] = self
    const res = context[key](...args) 
    /*
       传入的 this 可能是一个对象, 如果我们在其身上添加数据, 会出现意想不到的错误, 所以需要删除我们添加的属性
    */
    deleted context[key]
    return res
}

call

改变函数的 this 执向, 这个方法是在对象的原型链上的, 调用会执行目标函数, 第一个参数是需要指向的 this,后面的参数以 , 隔开,是函数的参数

// call 和 apply 的实现一致
function myCall (context, ...args) {
    if (typeof context !== 'function'){
        throw new Error('type error')
    }
    const self = this
    
    context = context === null || context === undefined ? globalThis : Object(context)
    
    const key = Symbol()   
    context[key] = self
    const res = context[key](...args)
    deleted context[key]
    return res   
}

bind

改变函数的 this 指向, 这个方法在对象的原型链上, 调用会返回一个新的函数, 不会执行目标函数, 第一个参数是需要执行的 this, 后面的参数以 , 隔开, 是函数的参数

function myBind(context, ...bindData) {
    if (typeof context !== 'function') {
        throw new Error('type error')
    }
    const self = this
    // 柯里化函数
    return function (..args) { // 返回的函数也可以接受参数
       const allParams = bindData.concat(args)
       return self.myApply(context, allParams) // 执行这个函数的返回值  
    }
}