从原生的角度理解call、apply、bind的实现

565 阅读3分钟

从原生的角度理解call、apply、bind的实现

call方法的实现

  function myFunction() {
       console.log(this.name);
    }

  let myObj = { name: "剑魂" };

  Function.prototype.myCall = function (obj) {
       console.log(this);   //这里的this指向的是谁?
    };
  myFunction.myCall(myObj);

首先我们需要明白,在上边的代码中我们的this指向的是谁?不明白的可以点击这里关于this的指向问题。我们可以知道此时当myFunction函数调用myCall的时候this指向myFunction函数。由此我们可以推断出以下写法:

function myFunction() {
    console.log(this.name);
}

function sum( job1, job2) {
   console.log("sum函数被执行", this, job1, job2);
   return job1 + '和' + job2 + this.name
}

let myObj = { name: "剑魂" };

Function.prototype.myCall = function (obj, ...newArr) {

    console.log(this); // 1.这里的this指向的是函数myFunction

    // 2.对obj转成对象类型(防止它传入得是非对象类型) 不然会报错
    obj = (obj !== null && obj !== undefined) ? Object(obj) : window

    obj.fn = this; // 3.给obj对象添加一个key为fn的方法,把函数myFunction赋值给fn次方法

    let result = obj.fn(...newArr); // 4.这里可以调用函数myFunction,得到函数内部打印为 “剑魂”

    delete obj.fn   // 5.删除对象obj中的fn,不让对象中多出新方法

    return result  // 6.将有返回值的函数返回值返回
};
myFunction.myCall(myObj); 
let result = sum.myCall(myObj, "肥鯮", "井盖", "不锈钢换盆")
console.log(result); // 肥鯮和井盖剑魂
到这里我们的call方法就完成了,是不是更加清楚了。从这里我们可以知道`call`的特点:
  1. call的参数是一共有2个,第一个为绑定this的是谁,第二个是一个参数列表。
  2. call方法调用完是立即执行,并且存在返回值。

apply方法的实现

`apply`方法的实现其实和call很相似,不同点就是参数处理方面不同,来往下看看吧:
// 实现自己的apply
Function.prototype.myapply = function (obj, newArray = []) {

    console.log(this); // 1.这里的this指向的是函数myFunction

    // 2.处理绑定的obj,防止传入不是一个对象报错
    obj = (obj !== null && obj !== undefined) ? Object(obj) : window

    // 3.把函数绑定到对象上
    obj.fn = this
    // 4.执行函数并得到函数的返回值
    let result = obj.fn(...newArray)
    delete obj.fn

    // 5.返回结果
    return result
}

function myFunction(name) {
     console.log(name);
}

function sum(job1, job2) {
    console.log("sum函数被执行", this, job1, job2);
    return job1 + '和' + job2 + this.name
}

let myObj = { name: "剑魂" };

// 自己实现调用
var result = sum.myapply('abc', ['肥鯮', '不锈钢换盆'])
console.log(result);


var result2 = myFunction.myapply("abc", ['剑魂'])
console.log(result2)

从这里我们可以知道`apply`的特点:
  1. apply的参数是一共有2个,第一个为绑定this的是谁,第二个是数组形式的对象参数。
  2. apply方法调用完是立即执行,并且存在返回值。

bind方法的实现

// 实现自己的apply
Function.prototype.mybind = function (obj, ...newArray) {

     console.log(this); // 1.这里的this指向的是函数myFunction
     let that = this
     // 2.处理绑定的obj,防止传入不是一个对象报错
     obj = (obj !== null && obj !== undefined) ? Object(obj) : window

     // 定义一个新的bind执行函数返回
     function proxyFn(...args) {
     obj.fn = that

     // 特殊:对两个传入的参数进行合并
     var arrList = [...newArray, ...args]
     // 执行函数得到返回值
     var result = obj.fn(...arrList)
     //  删除新增的函数
     delete obj.fn

     // 4.返回结果
     return result
     }
     // 5.返回结果
     return proxyFn
}

function myFunction(name) {
      console.log(name);
}

function sum(job1, job2) {
       return job1 + '和' + job2
}

// 自己实现调用
var result = sum.mybind('abc', '肥鯮', '不锈钢换盆')
console.log(result());

var result2 = myFunction.mybind("abc", '剑魂')
console.log(result2())
从这里我们可以知道 bind 的特点:
  1. bind的参数是一共有2个,第一个为绑定this的是谁,且只能改变一次函数的this指向,后续再用bind更改无效;第二个是参数形式的参数。
  2. bind方法调用成返回一个函数,需要再次调用此函数得到结果。