JS手写bind、call、apply

123 阅读3分钟

介绍

简单点,说法的方式需要尽量简单!在JS中,bindcallapply函数都是用来修改函数运行时的this指向

语法示例: 更加详细用法可以看一下 MDN文档

function greet(name) {
  console.log(`Hello, ${name}! My name is ${this.name}.`);
}
const person = { name: "John" };
greet.call(person, "Alice"); // 输出:Hello, Alice! My name is John.

区别

参数

  • bind 在设置this绑定值后,传入的参数为参数列表,并且可以多次传参
  • call 在设置this绑定值后,传入的参数为参数列表。
  • apply 在设置this绑定值后,传入的参数为数组。

执行时机

  • call、apply 立即执行
  • bind 没有立即执行,而是返回一个this固定指向目标的函数

this指向状态

  • apply、call 临时指向
  • bind 永久指向

手写bind函数

不是很完整但基本够用(mian试)

Function.prototype.bindMin = function(obj) {
  const fn = this

  if (typeof fn !== "function") return new TypeError("Error")
  const args = [...arguments].slice(1)
  return function Fbind() {
      return fn.apply(this instanceof Fbind ? new fn(...arguments) : obj, args.concat([...arguments]))
  }
}

分步解析

  1. 首先bind作为函数原型方法,它被实现在Function对象的原型上。所以我们手动实现也需要在Function对象的原型上定义。
  2. bindMin函数中的this指向谁?我们知道函数的指向是函数调用时才被确定的。bind是一个函数 方法,它是通过函数对象调用的,所以this指向调用它的函数对象。so,bindMin函数中的this将指向调用它的函数对象。
  3. 判断调用bindMin函数的调用者是否为函数!若不为函数者抛出一个错误。
  4. 由于bind函数第一个参数为this指向的目标,往后才是我们需要的参数,所以需要剪掉第一个参数。[...arguments].slice(1)
  5. 调用bind函数,返回一个已经改变this指向的函数。
  6. 当外部调用Fbind的时候会返回 fn.apply(括号中的内容暂时省略),这个立即调用函数。调用时进行判断,若外界是通过new Fbind()的方式调用的话,内部就new一个新的实例绑定this,否则就绑定到原来传入的obj

手写call函数

Function.prototype.callMin = function (obj = window) { // 函数默认参数解决调用不传参的问题
  
  // 判断调用者是否为函数
  if (typeof this !== "function") return new TypeError("Error")

  // 将函数(callMin的调用者)赋值给给obj对象的一个fn属性
  obj.fn = this
  // 获取参数列表
  const args = [...arguments].slice(1) 
  // 将调用obj对象上的fn属性(调用这个fn函数)并传入参数
  const res = obj.fn(...args) 
  delete obj.fn  
  return res
}

由于call和apply函数的this指向是临时的,所以在调用后需要删除掉obj对象上的fn属性。

为什么要把this赋值给传入的obj对象? 因为函数的this指向是在调用时被确定的,将this赋值给obj,调用fn属性时就是调用定义在对象中的函数。这个函数是通过对象调用的,所以this需要指向对象本身。so,need obj.fn = this

同理可实现apply!

手写apply函数

Function.prototype.callMin = function (obj = window) { 
  
  if (typeof this !== "function") return new TypeError("Error")

  obj.fn = this
  const args = arguments[1] || [] 
  const res = obj.fn(...args) 
  delete obj.fn  
  return res
}