JavaScript中apply、call和bind方法的原理与手动实现

作者:青柠时间:2026-10-24 23:19:56浏览:649

本文详细解析JavaScript中apply、call和bind三个内置方法的用法与区别,重点展示了如何在Function原型上手动实现这三个方法。通过代码示例说明它们如何显式绑定this及处理参数,帮助开发者深入理解函数调用机制。

1. 方法介绍

apply、call和bind均为JavaScript系统内置的方法,所有函数均可调用,因为它们被定义在Function.prototype上。这三者的核心作用是在函数调用时显式地指定this的指向。以下将分别介绍它们的基本用法。

apply方法:用于调用一个函数,该函数具有指定的this值,并且参数以数组(或类数组对象)的形式提供。

语法格式:func.apply(thisArg, [argsArray])

参数说明:thisArg表示在func函数调用时绑定的this值;[argsArray]是一个数组或类数组对象,其元素将作为独立参数传递给func函数。

使用效果:

function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.数组中的1 2 3分别传递给foo函数对应的三个参数
 */
foo.apply(obj, [1, 2, 3])

1. 方法介绍

call方法:用于调用一个函数,该函数具有指定的this值,并且参数以单独列出的形式(arg1, arg2, ...)提供。

语法格式:func.call(thisArg, arg1, arg2, ...)

参数说明:thisArg表示在func函数调用时绑定的this值;arg1, arg2, ...为指定的参数列表,将作为参数传递给func函数。

使用效果:

function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.call剩余参数中的a b c分别传递给foo函数对应的三个参数
 */
foo.call(obj, 'a', 'b', 'c')

1. 方法介绍

bind方法:创建一个新函数,当bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法格式:func.bind(thisArg[, arg1[, arg2[, ...]]])

参数说明:thisArg表示调用func函数时作为this参数传递给目标函数的值;arg1, arg2, ...为当目标函数被调用时,被预置入func函数的参数列表中的参数。

使用效果:

function foo(...args) {
  console.log(this, ...args)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.bind剩余参数中的1 2 3分别传递给foo函数中参数
 * 3.也可在newFoo调用时传入参数,这时bind传递的参数会与newFoo调用时传递的参数进行合并
 */
const newFoo = foo.bind(obj, 1, 2, 3)
newFoo()
newFoo('a', 'b', 'c')

1. 方法介绍

总结:

apply和call主要用于在函数调用时给函数的this绑定对应的值,两者作用类似,主要区别就是除了第一个参数,apply方法接受的是一个参数数组,而call方法接受的是参数列表。

bind也是给函数指定this所绑定的值,不同于apply和call的是,它会返回一个新的函数,新函数中的this指向就是我们所指定的值,且分别传入的参数会进行合并。

2. apply、call和bind方法的实现

为了使所有定义的函数能够使用我们自定义的apply、call和bind方法,所以需要将自己实现的方法挂在Function的原型上,这样所有的函数就可以通过原型链找到自定义的这三个方法了。

2.1. apply的实现

Function.prototype.myApply = function(thisArg, argArray) {
  // 1.获取当前需要被执行的函数
  // 因为myApply是需要被当前函数进行调用的,根据this的隐式绑定,此处的this就是指向当前需要被执行的函数
  const fn = this

  // 2.对传入的thisArg进行边界判断
  if (thisArg === null || thisArg === undefined) {
    // 当传入的是null或者undefined是,被执行函数的this直接指向全局window
    thisArg = window
  } else {
    // 将传入的thisArg对象化,方便后面在thisArg添加属性
    thisArg = Object(thisArg)
  }
  // 也可简单写成三元运算符:
  // thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.将获取的fn添加到thisArg对象上
  // 这里使用Symbol的原因是避免外部传入的thisArg中的属性与添加fn有冲突
  const fnSymbol = Symbol()
  Object.defineProperty(thisArg, fnSymbol, {
    enumerable: false,
    configurable: true,
    writable: false,
    value: fn
  })
  // 也可简单写成
  // thisArg[fnSymbol] = fn

  // 4.对argArray进行判断
  // 看是否有传入值,没有值传入就默认 []
  argArray = argArray || []

  // 5.调用获取的fn函数,并将对应传入的数组展开传递过去
  const result = thisArg[fnSymbol](...argArray)
  // 调用完后删除添加的属性
  delete thisArg[fnSymbol]

  // 6.将结果返回
  return result
}

测试:虽然打印出来的对象中还存在Symbol属性,实际上已经通过delete删除了,这里是对象引用的问题。

function foo(x, y, z) {
  console.log(this, x, y, z)
}

foo.myApply({name: 'curry'}, [1, 2, 3])

1. 方法介绍

2.2. call的实现

call方法的实现和apply方法的实现差不多,主要在于后面参数的处理。

Function.prototype.myCall = function(thisArg, ...args) {
  // 1.获取当前需要被执行的函数
  const fn = this

  // 2.对传入的thisArg进行边界判断
  thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.将获取的fn添加到thisArg对象上
  const fnSymbol = Symbol()
  thisArg[fnSymbol] = fn

  // 4.调用获取的fn函数,并将对应传入的args传递过去
  const result = thisArg[fnSymbol](...args)
  // 调用完后删除添加的属性
  delete thisArg[fnSymbol]

  // 5.将结果返回
  return result
}

测试:

function foo(x, y, z) {
  console.log(this, x, y, z)
}

foo.myCall({name: 'curry'}, 1, 2, 3)

1. 方法介绍

2.3. bind的实现

bind方法的实现稍微复杂一点,需要考虑到参数合并的问题。

Function.prototype.myBind = function(thisArg, ...argsArray) {
  // 1.获取当前的目标函数,也就是当前使用myBind方法的函数
  const fn = this

  // 2.对传入的thisArg进行边界判断
  thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  // 3.将获取的fn添加到thisArg对象上
  const fnSymbol = Symbol()
  thisArg[fnSymbol] = fn

  // 4.定义一个新的函数
  function newFn(...args) {
    // 4.1.合并myBind和newFn传入的参数
    const allArgs = [...argsArray, ...args]
    // 4.2.调用真正需要被调用的函数,并将合并后的参数传递过去
    const result = thisArg[fnSymbol](...allArgs)
    // 4.3.调用完后删除添加的属性
    delete thisArg[fnSymbol]

    // 4.4.将结果返回
    return result
  }

  // 6.将新函数返回
  return newFn
}

测试:

function foo(x, y, z) {
  console.log(this, x, y, z)
}

const newFoo = foo.myBind({ name: 'curry' }, 1, 2)
newFoo(3)

1. 方法介绍

总结

到此这篇关于使用JS简单实现apply、call和bind方法的文章就介绍到这了,更多相关JS实现apply、call和bind方法内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章