JS 手写call和apply

144 阅读2分钟

JS 手写call和apply

call和apply都是改变this指向的方法,但是call和apply又有所不同

不同之处是:

call() 方法分别接受参数。

apply() 方法接受数组形式的参数。

以Math.max为例,Math.max是分别接受参数,返回最大值,我们分别手写call和apply方法,实现原生Math.max方法和接受数组形式参数的Math.max方法

注意

首先明确apply函数是定义在Function.prototype上的,为所有Function类型的对象所共享。

思路解析

手写call

  1. 明确this,this就是调用call/apply的原函数
  2. 判断this是否是函数类型,如果不是则抛出错误
  3. 确定执行上下文context,也就是调用call/apply传入的第一个参数,如果没有则指向window
  4. 获取剩余参数,[...argument]把伪数组转成数组,删除第一个参数
  5. 给context添加一个方法fn,赋值为this,这样调用context.fn时,this这个函数里面的this就会指向context,调用context.fn时把args解构出来作为参数传进去。
  6. 然后删除context的fn方法,以免污染context的属性
  7. 返回调用context.fn得到的值
Function.prototype.MyCall = function(context){
    if(typeof this !== "function"){
        throw new TypeError("error")
    }
​
    context = context || window
​
    context.fn = this
​
    const args = [...arguments].slice(1)
​
    const result = context.fn(...args)
​
    delete context.fn
​
    return result
}
console.log(Math.max.MyCall(null, 1,3,2))

手写apply

  1. 明确this,this就是调用call/apply的原函数
  2. 判断this是否是函数类型,如果不是则抛出错误
  3. 确定执行上下文context,也就是调用call/apply传入的第一个参数,如果没有则指向window
  4. 获取第二个参数args,也就是一个数组,如果没有传参,则为空数组
  5. 给context添加一个方法fn,赋值为this,这样调用context.fn时,this这个函数里面的this就会指向context,调用context.fn时把args解构出来作为参数传进去。
  6. 然后删除context的fn方法,以免污染context的属性
  7. 返回调用context.fn得到的值
Function.prototype.MyApply = function(context){
     // this就是谁调用的MyApply,谁就是这里的this
    //  要先判断this是不是function
    if(typeof this !== 'function'){
        throw new TypeError("error")
    }
    // 是否有上下文
    // 如果没有上下文就是window
    context = context || window
   
​
    // apply的特性的参数是数组
    const args = arguments[1] || []
    context.fn = this
    const result = context.fn(...args)
    delete context.fn
    return result
}
​
console.log(Math.max.MyApply(null, [1,3,2]))