使用JS简单实现一下apply、call和bind方法

1.方法介绍

apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(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])

  • call方法:使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。

    • 使用语法: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')

  • 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')

总结:

  • 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])

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)

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. Vc6.0头文件的定义
  2. Centos 基础开发环境搭建之Maven私服nexus
  3. Windows系统自带工具的 cmd 命令
  4. Linux下安装vsftpd
  5. SpringMVC案例1——对User表进行CRUD操作
  6. sonar的安装与代码质量检测实例
  7. OC编写使用调试器
  8. LineNumberReader类
  9. Mysql 复制表结构 及其表的内容
  10. django model Foreign key usage 关系型数据库 ORM
  11. Urlparse模块
  12. 再回首数据结构—数组(Golang实现)
  13. leetcode — interleaving-string
  14. char
  15. CentOS6.9切换root用户su root输入正确密码后一直提示Incorrect password,如何解决?
  16. 常用的npm指令总结
  17. mipsel汇编指令学习
  18. mongodb千万级写入怎么优化
  19. 杨晓峰-Java核心技术-9 HashMap Hashtable TreeMap MD
  20. 1z0-052 q209_4

热门文章

  1. SQL高级优化系列
  2. Nginx日志通过Flume导入到HDFS中
  3. idea同时启动多个微服务模块进行管理
  4. 输出前 n 个Fibonacci数
  5. Hello world.java
  6. 你管这叫代理模式(Proxy Pattern)
  7. Hybrid App(混合开发) 移动端开发调试
  8. 1月29日 体温APP开发记录
  9. nao机器人使用手册
  10. vuecli学习01 - 环境搭建