一文带你了解并实现 call、apply、bind

325 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

前言

众所周知call apply bind都可以改变函数调用的this指向,从而改变执行上下文的。今天我们就来看看三者有什么区别、如何使用以及实现原理?

call

我们可以通过call来改变this指向,call方法挂载在Function的原型链中的Function.prototype.call()。在MDN中是这样介绍的call()  方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

基本使用

let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this); // person
  console.log(this.name, age, sex); // 小飞仔 25 男
}
sayHi.call(person, 25, '男');

image.png

代码大致执行过程是这样的。首先寻找call方法,让sayHi方法中的this变为第一个参数值person,最后再把sayHi这个函数执行。

我们在试一下thisArg传null会返回Window对象

function sayHi() {
  console.log(this); // Window 
}
sayHi.call(null);

image.png

我们在试一下thisArgundefined会返回Window对象

function sayHi() {
  console.log(this); // Window 
}
sayHi.call(undefined);

image.png

我们在试一下thisArg0会返回Nunber{0}对象

function sayHi() {
  console.log(this); // `Nunber{0} 
}
sayHi.call(0);

image.png

源码实现

Function.prototype.myCall = function (thisArg, ...args) {
  if (typeof thisArg === 'object') {
    thisArg = thisArg || window
  } else {
    thisArg = new Object(thisArg)
  }
  thisArg.fn = this
  let result = thisArg.fn(...args)
  thisArg.fn = null
  return result;
}
let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this); // person
  console.log(this.name, age, sex); // 小飞仔 25 男
}
sayHi.myCall(person, 25, '男');

apply

我们可以通过apply来改变this指向,apply方法挂载在Function的原型链中的Function.prototype.apply()。在MDN中是这样介绍的apply()方法调用一个具有给定this值的函数,以及以一个数组的形式提供的参数。

let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.apply(person, [25, '男']); // 小飞仔 25 男

源码实现

Function.prototype.myApply = function (thisArg, ...args) {
   if (typeof thisArg === 'object') {
    thisArg = thisArg || window
  } else {
    thisArg = new Object(thisArg)
  }
  thisArg.fn = this
  thisArg.fn(...args)
  delete thisArg[fn]
}
let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.myApply(person, [25, '男']); // 小飞仔 25 男

bind

我们可以通过bind改变this指向,apply方法挂载在Function的原型链中的Function.prototype.apply()。在MDN中是这样介绍的MDN 是这样描述的bind()  方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.bind(person, 25, '男')(); // 小飞仔 25 男

源码实现

Function.prototype.myBind = function (thisArg,innerArgs) {
    var _this = this
    return function (...finnalyArgs) {
        return _this.call(thisArg,...innerArgs,...finnalyArgs)
    }
}
let person = {
  name: '小飞仔'
}
function sayHi(age,sex) {
  console.log(this.name, age, sex);
}
sayHi.apply(person, [25, '男']); // 小飞仔 25 男

总结

call、apply是一个数组。大致相同区别在于call参数不同一个是对象一个是数组。bind则返回一个新的对象。