• JS 手写call、apply和bind方法


    一、方法介绍

    apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(Function.prototype),而他们的作用都是给我们函数调用时显式绑定上this。下面先介绍一下它们的基本用法:

    1.1 call 方法

    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    1.2 apply 方法

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    1.3 bind

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    二、方法的实现

    function foo(...arg) {
      console.log(this.name, ...arg);
    }
    
    function obj() {
      this.name = 'obj'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.1 call 方法

    Function.prototype.myCall = function(context, ...args) {
      // 1. 检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
      context = context || window;
    
      // 2. 为上下文对象创建一个唯一属性来保存当前函数,
      // 以便在调用完后删除这个属性,避免污染原始对象
      const fnKey = Symbol();
      context[fnKey] = this;
    
      // 3. 通过上下文对象调用函数并传入参数
      const result = context[fnKey](...args);
    
      // 4. 删除添加的属性
      delete context[fnKey];
    
      // 返回函数调用结果
      return result;
    };
    
    // 原始call方法
    foo.call(obj, 1, 2, 3)
    // 手写call方法
    foo.myCall(obj, 1, 2, 3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    输出结果:
    在这里插入图片描述

    2.2 apply 方法

    Function.prototype.myApply = function (context, args) {
      // 1.检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
      context = context || window;
    
      // 2. 为上下文对象创建一个唯一属性来保存当前函数,
      // 以便在调用完后删除这个属性,避免污染原始对象
      const fnKey = Symbol();
      context[fnKey] = this;
    
      let result;
      // 3. 检查是否传入了参数
      if (args) {
        // 使用扩展运算符展开参数数组,并通过上下文对象调用函数
        result = context[fnKey](...args);
      } else {
        // 如果未传入参数则直接通过上下文对象调用函数
        result = context[fnKey]();
      }
    
      // 4. 删除添加的属性
      delete context[fnKey];
    
      // 返回函数调用结果
      return result;
    };
    
    foo.apply(obj, [1, 2, 3])
    foo.myApply(obj, [1, 2, 3])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    输出结果:
    在这里插入图片描述

    2.3 bind 方法

    Function.prototype.myBind = function(context, ...args) {
      const fn = this;
    
      return function(...innerArgs) {
        // 使用 apply 方法将传入的上下文和参数传递给函数,并返回调用结果
        return fn.apply(context, [...args, ...innerArgs]);
      };
    };
    let fn = foo.bind(obj,1,2,3)
    let fn2 = foo.myBind(obj,1,2,3)
    fn('a','b','c')
    fn2('a','b','c')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出结果:
    在这里插入图片描述

  • 相关阅读:
    小程序源码:王者改名微信小程序源码下载另一版本支持流量主收益
    (10)在以下横线处写出使用v_stu_m视图查询总学分大于10的学生的查询语句,查询 果如图9.12所示。
    Win11怎么添加pdf虚拟打印机
    RabbitMQ 服务启动失败问题小结(Windows环境)
    【Unity】LayoutGroup自动缩放子对象大小
    一篇全网最详细的可视化图表Python库,学到就赚到了
    aardio 调用 vs 编写的dll (cdecl方式和stdcall方式)
    【入门篇】UML-ClassDiagram类图
    java优先级队列PriorityQueue
    windows 电脑改成安卓桌面
  • 原文地址:https://blog.csdn.net/wu_2004/article/details/133122756