• 【前端手写】call和apply方法


    call和apply都是Function原型prototype上的方法。
    用于改变this指向
    两者不同点在于传参。
    call需要传递散参,而apply需要传递一个参数数组。
    这两个方法相比bind有一个区别,在于这两个方法是直接调用的,而bind方法是返回一个函数,而这个函数的this指向是明确的。

    我们先来看看call方法有哪些注意点。

    function a(...args) {
      console.log(this, args);
    }
    
    a.call({ a: 1 }, 1, 2, 3);
    // { a: 1 } [ 1, 2, 3 ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当然如果我们绑定的this如果不是对象的话,那么其实call方法也会转成对象

    function a(...args) {
      console.log(this, args);
    }
    
    a.call(1, 1, 2, 3); // [Number: 1] [ 1, 2, 3 ]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    大概知道他的一些细节后,我们来手写一下call方法

    Function.prototype.myCall = function (_this, ...args) {
      // call方法可以用来改变方法调用的this指向
      const func = this;
      if (typeof func !== "function") {
        // 类型判断
        throw TypeError("this is not a function");
      }
      if (typeof _this !== "object") {
        // 如果绑定的this不是一个对象
        _this = Object(_this);
      }
      _this.fn = func;
      const result = _this.fn(...args);
      // 这里在_this上添加了一个属性,需要删除
      delete _this.fn;
      return result;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里我们测试一下方法的实现是否和call方法一致

    function a(...args) {
      console.log(this, args);
    }
    
    a.myCall(1, 1, 2, 3);
    // [Number: 1] { fn: [Function: a] } [ 1, 2, 3 ]
    
    
    function a(...args) {
      console.log(this, args);
    }
    
    a.myCall({ a: 1 }, 1, 2, 3);
    // { a: 1, fn: [Function: a] } [ 1, 2, 3 ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里其实有一个小瑕疵,就是如果函数中有用到this中的一个属性fn那么就会有问题了,并且打印出来也会有问题
    但是基本功能已经很好了
    但如果你是一个完美主义的人
    那我们不妨继续完善代码

    Function.prototype.myCall = function (_this, ...args) {
      // call方法可以用来改变方法调用的this指向
      const func = this;
      if (typeof func !== "function") {
        // 类型判断
        throw TypeError("this is not a function");
      }
      if (typeof _this !== "object") {
        // 如果绑定的this不是一个对象
        _this = Object(_this);
      }
      const random = Math.random().toString(16).substring(2);
      _this["fn" + random] = func;
      const result = _this["fn" + random](...args);
      // 这里在_this上添加了一个属性,需要删除
      delete _this['fn' + random];
      return result;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    生成一个随机字符串来防止属性重复

    啥?还不完美吗?

    Function.prototype.myCall = function (_this, ...args) {
      // call方法可以用来改变方法调用的this指向
      const func = this;
      if (typeof func !== "function") {
        // 类型判断
        throw TypeError("this is not a function");
      }
      if (typeof _this !== "object") {
        // 如果绑定的this不是一个对象
        _this = Object(_this);
      }
      const random = Math.random().toString(16).substring(2);
      Object.defineProperty(_this, "fn" + random, {
        value: func,
        enumerable: false,
      });
      const result = _this["fn" + random](...args);
      // 这里在_this上添加了一个属性,需要删除
      delete _this.fn;
      return result;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    function a(...args) {
      console.log(this, args);
    }
    
    a.myCall({ a: 1 }, 1, 2, 3);
    // { a: 1 } [ 1, 2, 3 ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    Function.prototype.myApply = function (_this, args) {
      const func = this;
      if (typeof func !== "function") {
        // 类型判断
        throw TypeError("this is not a function");
      }
      if (!Array.isArray(args)) {
        throw TypeError("arg is not a array");
      }
      return func.myCall(_this, ...args);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我的github有更多的前端手写代码。

  • 相关阅读:
    0-1背包问题
    小车测距避障-通过串口(可蓝牙)控制
    OpenAI正式发布第一个官方.NET版本库的测试版
    netty系列之:来,手把手教你使用netty搭建一个DNS tcp服务器
    Leetcode 199. Binary Tree Right Side View (DFS/BFS好题)
    【PAT(甲级)】1045 Favorite Color Stripe(映射转换成FIS)
    【Computer Vision Foundation】全球计算机视觉基金会论文网
    Vite+Vue3+TS项目创建及基本环境搭建
    动态规划之空间压缩技巧
    Linux进程控制/进程终止
  • 原文地址:https://blog.csdn.net/weixin_45696837/article/details/126501887