JavaScript系列之手写call与apply

161 阅读2分钟

了解Function.prototype.call()

手写call

首先我们来看这段代码

let foo = {
     value:1
 }
 function bar(){
  console.log(this.value)
}
 bar.call(foo)    // 控制台输出1

我们来梳理一下call的作用

  1. call会改变this的指向,谁调用它就会指向谁
  2. 会执行bar这个函数

相当于执行了 这个对象里面的这个方法

let foo = {
     value:1,
     bar:()=>{
        console.log(this.value)
     }
 }
foo.bar()


这个时候 this 就指向了 foo,但是这样却给 foo 对象本身添加了一个属性,所以们用 delete 再删除它即可。

所以我们模拟的步骤可以分为:

1.  将函数设为对象的属性;
2.  执行该函数;
3.  删除该函数;
foo.fn = bar
foo.fn()
delete foo.fn

然后我们按照这个步骤模拟写一下call这个方法给他挂在原型上

Function.prototype.call2 = (context)=>{
console.log(this)
context.fn = this  //这里的this指的是调用call2的函数对象,比如例子中的bar函数。这一步是将该函数赋值给context对象的一个属性fn,这样在context上就可以通过fn来调用这个函数了。
context.fn();
delete context.fn;
}

这个是没有参数的形式,我们的call方法一般都有参数,而且一般都有多个参数比如说bar.call(foo, 'kevin', 18);如果考虑到有参数的话我们要怎么实现呢,请看下面这段代码

Function.prototype.call2 =  function(context,...arguments) {
     console.log(...arguments)
     context.fn = this
     context.fn(...arguments);
     delete context.fn;
}
let foo = {
     value: 1
}
function bar(name,age) {
     console.log(name,age)
     console.log(this.value)
}

bar.call2(foo,'kaiwen',123) // kaiwen 123 1  

我们以es6的...arguments来代替后面所有的参数,之后再传进执行函数fn里去就可以了,...arguments是一个类似于数组的东西,当然这种写法我们没有考虑到函数有返回值的情况,有返回值的情况请看接下来的代码

Function.prototype.call2 = function (context, ...arguments) {
     context.fn = this
     let result = context.fn(...arguments);
     delete context.fn;
     return result
}
let foo = {
     value: 1
}
function bar(name, age) {
     return {
          value: this.value,
          name,
          age
     }
}
console.log(bar.call2(foo, 'kaiwen', 123))   // {value:1,name:'kaiwen',age:123}

这就是手写call的全部了

手写apply

手写apply和手写call类似就是传的参数不一样第二个参数是数组

Function.prototype.apply2 = function (context, arr) {
    context.fn = this
    if(arr.length){
     result = context.fn(arr);    
    }else {
        result = context.fn(); 
    }
    delete context.fn;
    return result
}
let foo = {
    value: 1
}
function bar(arr) {
    console.log(arr)
    return {
         value: this.value,
         arr
    }
}
console.log(bar.apply2(foo, [1,2,3]))