首页/文章/javascript

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

2024-11-22
12825 分钟
...

文章介绍了 JavaScript 中 apply、call 和 bind 方法,apply 以数组形式传参,call 以参数列表传参,两者用于函数调用时绑定 this 值,bind 会返回新函数并处理参数合并。还讲解了这三个方法的自定义实现及测试示例。

关联问题: apply和call区别在哪 bind如何合并参数 怎样实现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.myCall = function(thisArg, ...args) {
      
      const fn = this;

      
      thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg);

      
      
      const fnSymbol = Symbol();

      Object.defineProperty(thisArg,fnSymbol,{
        enumerable: false,
        configurable: false,
        writable: false,
        
        
        value : this
      })

      
      

      
      args = args || []

      
      

      
      
      


      
      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.myApply = function (thisArg, args) {
        const fn = this
        thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
        const fnSymbol = Symbol()
        Object.defineProperty(thisArg, fnSymbol, {
          enumerable: false,
          configurable: false,
          writable: false,
          value: fn
        })
        const result = thisArg[fnSymbol](...args)
        delete thisArg[fnSymbol]
        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) {
  
  const fn = this

  
  thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)

  
     const fnSymbol = Symbol();
      Object.defineProperty(thisArg,fnSymbol,{
        enumerable: false,
        configurable: false,
        writable: false,
        value:this
      })

  
  
  
  
  function newFn(...args) {
    
    const allArgs = [...argsArray, ...args]
    
    const result = thisArg[fnSymbol](...allArgs)
    
    delete thisArg[fnSymbol]

    
    return result
  }

  
  return newFn
}

测试:

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

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

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-10
手把手教你实现一个EventEmitter
你是不是也遇到过这种情况:代码里各种事件监听和触发,回调函数套了一层又一层,最后自己都理不清哪个事件先触发、哪个后触发了? 别担心,今天我就带你从零开始实现一个自己的事件触发器(EventEmitter),让你彻底掌握事件驱动的精髓,代码清晰度直接提升200%! 为什么要懂EventEmitter? 先说说为什么我们要关心这个。Node.js的核心就是事件驱动,比如文件读写、网络请求,都是通过事件来处理的。不会EventEmitter,...
学习面试
javascript2025-08-09
Web Worker 多线程魔法
你是否曾遇到过网页因为复杂计算而卡顿的情况?今天,让我们一起探索Web Worker的神奇世界,看看如何用多线程技术解决性能瓶颈,并实现流畅的图片压缩功能! 为什么需要Web Worker? 想象一下,你在一个繁忙的餐厅里,只有一位服务员(主线程)负责点餐、上菜、结账所有工作。当客人突然暴增时,这位服务员就会手忙脚乱,其他客人只能干等着… 这就是JavaScript的现状—— 单线程 !所有任务都在一个线程中排队执行,一旦遇到复杂计算(...
学习面试
javascript2025-08-07
🌐AST抽象语法树
AST 是前端开发中一个非常重要的概念,它就像是代码的“编译器”,能够帮助我们理解和操作代码。本文将深入探讨 AST 的原理和应用,带你从零开始了解 AST。 一、什么是 AST? AST(抽象语法树)是一种树形结构,用于表示程序代码的语法结构。它将代码分解为一系列的节点,每个节点代表代码中的一个元素,如变量、函数、表达式等。AST 是编译器和解释器的基础,也是代码分析和转换的关键工具。 1\. AST 的结构 AST 的结构通常包括以...
学习面试
javascript2025-08-05
打字机效果是如何实现的
分析效果 使用 AI 对话的时候,文字是一个字一个字蹦出来的?它不是等所有内容生成完毕后一次性展示,而是以一种流畅、实时的“打字机”效果呈现,这种体验的背后,隐藏着一种高效的Web技术。它就是 服务器推送事件(Server Sent Events,简称 SSE) 什么是 SSE 从根本上说,SSE 是一种允许服务器 单向 、 持续 地向客户端(浏览器)推送数据的Web技术。 可以把它想象成一个 电台广播 : 服务器 是广播电台,持续不断...
学习面试
javascript2025-08-04
Promise 从原理到实现
前言 是 异步编程的核心概念之一,它为我们提供了一种更优雅的方式来处理异步操作。 本文将全面剖析 的工作原理,并逐步实现一个符合 规范的 。 正文 一、Promise 基础概念 代表一个异步操作的最终完成(或失败)及其结果值。它有 种状态: 1. pending : ,既不是成功,也不是失败状态 2. fulfilled :意味着操作 完成 3. rejected :意味着操作 状态一旦改变就不能再变,只能从 变为 ,或从 变为 。 二...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录