call、apply 以及 bind 的区别和用法

call、apply、bind

call和apply共同点

  • 共同点:改变函数执行时的上下文,将一个对象的方法交给另一个对象来执行,并且是立即执行的
  • call和apply的对象,必须是一个函数Function

call和apply区别

call的写法

需要注意以下几点:

  • 调用call的对象,必须是个函数Function
  • call的第一个参数,是一个对象,Function的调用者,将会指向这个对象。如果不传,则默认为全局对象window
  • 第二个参数开始,可以接收任意个参数,每个参数会映射到相应位置的Function的参数上。
  • 但是如果将所有的参数作为数组传入,他们会作为一个整体映射到Function对应的第一个参数上,之后参数都为空
  1. function func(a,b,c) {}
  2. func.call(obj, 1,2,3) // func接收到的参数为1,2,3
  3. func.call(obj, [1,2,3]) // func接收到的参数为[1,2,3], undefined, undefined

apply的写法

需要注意的是:

  • 它的调用者必须是函数Function,并且只接受两个参数,第一个参数的规则与call一致
  • 第二个参数,必须是数组或者类数组,他们会被转换成类数组,传入到Function中,并且会被映射到Function对应的参数上,
    1. func.apply(obj, [1,2,3]) // func接收到的参数是1,2,3
    2. func.apply(obj, {0:1, 1:2, 2:3, length: 3}) // func接收到的参数是1,2,3

什么是类数组

  • 具备与数组特征类似的对象
  • 类数组无法使用forEach、splice、push等数组原型链上的方法

call和apply的用途

call的使用场景

  • 对象的继承

    1. function superClass() {
    2. this.a = 1
    3. this.print = function() {
    4. console.log(this.a)
    5. }
    6. }
    7. function subClass() {
    8. superClass.call(this)
    9. this.print()
    10. }
    11. subClass() // 1
  • 借用方法

    • 类数组,如果想使用Array原型链上的方法
    • let demoNodes = Array.prototype.slice.call(document.getElementsByTagName("*"))

apply 的用法

  • Math.max,用来获取数组中最大的一项
    • let max = Math.max.apply(null, array)
  • 实现两个数组合并
    1. let arr1 = [1, 2, 3]
    2. let arr2 = [4, 5, 6]
    3. Array.prototype.push.apply(arr1, arr2)
    4. console.log(arr1) // [1, 2, 3, 4, 5, 6]

bind的使用

Function.bind(thisArg, arg1, arg2])

  • bind方法与apply、call比较类似,也能改变函数体内的this指向,不同的是,bind方法的返回值是函数,并且需要稍后调用,才会执行。而apply和call则是立即调用
  • 如果bind的第一个参数是null或者undefined,this就指向全局对象window
  1. function add (a, b) {
  2. return a + b;
  3. }
  4. function sub (a, b) {
  5. return a - b;
  6. }
  7. add.bind(sub, 5, 3); // 这时,并不会返回 8
  8. add.bind(sub, 5, 3)(); // 调用后,返回 8

总结

  • call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别。

  • bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并且需要稍后再调用一下,才会执行。

bind的模拟实现

bind

它的特点:

  1. 返回一个函数;
  2. 可以传入参数

实现bind最终代码

  1. Function.prototype.bind1 = function(context) {
  2. if (typeof this !== function) {
  3. throw new Error(Function.prototype.bind --what is trying to be bound is not callable)
  4. }
  5. var self = this
  6. var args = Array.prototype.slice.call(arguments, 1)
  7. var fNOP = function () {}
  8. var fBound = function() {
  9. var bindArgs = Array.prototype.slice.call(arguments)
  10. // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
  11. // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
  12. // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
  13. return self.apply(this instanceof
  14. fNOP ? this : context, args.concat(bindArgs))
  15. }
  16. fNOP.prototype = this.prototype
  17. fBound.prototype = new fNOP()
  18. return fBound
  19. }

参考资料


文章标签:

原文连接:https://juejin.cn/post/7110793865400942623

相关推荐

Webpack学习系列 - Webpack5 怎么集成Babel ?

我在淘宝做弹窗,2022 年初的回顾与展望

看完这篇,你也可以搞定有趣的动态曲线绘制

低代码平台的属性面板该如何设计?

34个图片压缩工具集合,包含在线压缩和CLI工具

冴羽答读者问:过程比结果重要吗?如果是,怎么理解?如果不是,又怎么解?

中杯超大杯中间的新选择——vue2.7+vite+ts实践

LiveData源码分析

亚马逊Prime:流媒大战杀手锏

Vue详解知识概括

基于 Docker 来部署 Vue 或 React 前端项目及 Node 后端服务

完美解决自定义字体垂直方向上下偏移的问题

使用vuepress从零开始搭建我的技术文档|已部署到线上

【Vue.js 3.0源码】AST 转换之节点内部转换

小程序+电商,该如何寻找营销增长点

前端如何开始深度学习,那不妨试试JAX

你可能不知道的 前端浏览器(window) 对本地文件操作(File System Access API)

爱奇艺向抖音开启授权,打开内容价值的新大门

使用ComposeDesktop开发一款桌面端多功能APK工具

一个简洁、强大、可扩展的前端项目架构是什么样的?