JavaScript中call,apply,bind实现

我们都知道,call(),apply(),bind()都可以改变函数内部的this指向,那他们到底是如何实现的呢,下面我们用js来模拟实现,为什么说是模拟实现呢?因为我们的js引擎,主流的v8引擎源代码是通过c++编写的,所以我们只能说是模拟实现,但是实现的功能都是一样的,但是一些edge case可能会考虑的不是很充分。

call实现

Function.prototype.hqcall = function(thisArg, ...args) { // 剩余参数
  const fn = this  // 因为再调用函数的时候采用的是隐式调用,所以this会指向调用这个函数(hqcall)的函数(foo)

  // 判断传进来的值,如果是null或undefined 则指向window
 thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

  // 再内部可以直接使用call修改this指向
  // fn.call(thisArg)
  thisArg.fn = fn   // 可以先将这个函数添加到对象中
  const result = thisArg.fn(...args) // 展开运算符 // 然后通过对象的方式进行调用  (隐式绑定
  
  delete thisArg.fn 

  return result
}

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

const res = foo.hqcall({name: 'lhq'}, 20, 30)
console.log(res)
// foo.hqcall(123)
// foo.hqcall(null)
// foo.hqcall(undefined)
// foo.call(123)


/**
 * Obejct()
 *  这个方法会将传入的值转成对应的对象
 */

// console.log(Object('test'))

apply实现

// 手动实现apply

Function.prototype.hqapply = function(thisArg, arr) {
  // 1.获取到要执行的函数
  const fn = this
  // 2.处理绑定的thisArg参数
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  // 3.将要执行的函数添加到对象中
  thisArg.fn = fn

  // 4.执行函数,传入参数并拿到返回值
  // let result = null
  // if (!arr) { // 没有传递参数
  //   result = thisArg.fn()
  // } else { // 有传递参数
  //   result = thisArg.fn(...arr)
  // }

  // arr = arr ? arr : []
  arr = arr || []
  const result = thisArg.fn(...arr)
  // 5.删除对象中保存的方法
  delete thisArg.fn

  // 6.返回最终的结果
  return result
}

function foo(num1, num2) {
  console.log('foo被执行了')
  console.log(this)
  return num1 + num2
}

function bar() {
  console.log('bar执行了')
}

const res = foo.hqapply({name: 'hq'}, [20, 60])
bar.hqapply({name: 'hq'})

bind实现

Function.prototype.hqbind = function(thisArg, ...argArray) {
  // 1.获取到要执行的函数
  const fn = this

  // 2.处理绑定的thisArg参数
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  
  return function proxyFn(...args) {
    // 3.将函数放到thisArg中进行使用
    thisArg.fn = fn
    // 4.对两个传递进来的参数合并
    const finalAgs = [...argArray, ...args]

    const result = thisArg.fn(...finalAgs)
    delete thisArg.fn

    // 返回结果
    return result
  }
}

function foo() {
  console.log('执行了foo')
}

const bar = foo.hqbind({name: 'lhq'})
bar()
foo.bind()

function num(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4)
}
const res = num.bind({}, 10, 20)
res(30, 40)
/**
 * bind 传递参数的时候可以分开传递,在bind调用的时候传递,再返回的函数调用时继续传递参数
 * 
 */

猜你喜欢

转载自blog.csdn.net/Liushiliu104/article/details/124070596
今日推荐