call-bind-apply | 青训营笔记

57 阅读2分钟

在JavaScript中函数的调用除了可以直接调用,还可以为他绑定特定的this值,使用call、bind、apply 手写实现一个

call

首先看看系统的call

const obj = {name: "Allen"}

function sum(num1, num2) {
  console.log("this", this)
  return num1 + num2
}

sum.call(obj)			// this { name: 'Allen' }
sum.call("abc")		// this [String: 'abc']

接下来实现一下 要让所有的函数都拥有这个mycall方法,所以我们要在Function的原型链上添加 Function.prototype.mycall = function(){}

既然是添加到函数上的属性,到时候调用是通过对象的方法来调用的,例如sum.mycall() 作为对象的方法调用其 this 值会进行隐式绑定绑定到调用它的对象上,也就是调用它的函数,这样就可以获取到函数从而在mycall中对它进行调用

Function.prototype.mycall = function(thisArgs, ...rest) {
  // 开始还需要判断传进来的是不是undefined和null,如果不是就进行Object处理
  // 因为传进来的有可能是数字和字符串,而他们是不能添加属性的
  thisArgs = thisArgs ? Object(thisArgs) : window
  thisArgs.fn = this
  const result = thisArgs.fn(...rest)
  delete thisArgs.fn
  return result
}

function sum(num1, num2) {
  console.log("this", this)
  return num1 + num2
}

const obj = {name: "Allen"}

const result1 = sum.mycall(obj, 1, 2)
const result2 = sum.mycall("abc", 1, 2)

结果如下:

image.png

实现成功

apply

apply和call是差不多的,就是传的参数第二个参数是要求为数组

Function.prototype.myapply = function(context, argArray) {
  context = context ? Object(context) : window
  context.fn = this
  let result
  if(argArray) {
    result = context.fn(...argArray)
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

function sum(num1, num2) {
  console.log("this", this)
  return num1 + num2
}
const obj = {name: "Allen"}

const result = sum.myapply(obj, [20, 30])
console.log(result)

image.png

bind

bind和上面两个差别有些大,先看看系统的bind

function sum(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4)
}

const obj = {name: "Allen"}

const newSum = sum.bind(obj, 10, 20)
newSum(30, 40)		// 10 20 30 40

可以看到它这个传参数可以分开传,可以在bind的时候传一些,获取到新函数进行调用再传完剩余的参数

开始实现

Function.prototype.mybind = function(context, ...args) {
  context = context ? Object(context) : window
  context.fn = this

  return function(...rest) {
    const finalArgs = [...args, ...rest]
    const result = context.fn(...finalArgs)
    return result
  }
}

function sum(num1, num2, num3, num4) {
  console.log(this)
  console.log(num1, num2, num3, num4)
  return num1 + num2 + num3 + num4
}

const obj = {name: "Allen"}

const newSum = sum.mybind(obj, 10, 20)
newSum(30, 40)

image.png

注意bind这个函数最后不能把 context.fn 删除属性,否则得到的新函数只能调用一次