使用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)

使用JS简单实现一下apply、call和bind方法的更多相关文章

  1. 彻底理解了call()方法,apply()方法和bind()方法

    javascript中的每一个作用域中都有一个this对象,它代表的是调用函数的对象.在全局作用域中,this代表的是全局对象(在web浏览器中指的是window).如果包含this的函数是一个对象的 ...

  2. JS中的call、apply、bind方法

    JS中的call.apply.bind方法 一.call()和apply()方法 1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]] ...

  3. JS中call、apply的用法说明

    JS Call()与Apply()的区别 ECMAScript规范给所有函数都定义了Call()与apply()两个方法,call与apply的第一个参数都是需要调用的函数对象,在函数体内这个参数就是 ...

  4. js中call、apply、bind那些事

    前言 回想起之前的一些面试,几乎每次都会问到一个js中关于call.apply.bind的问题,比如- 怎么利用call.apply来求一个数组中最大或者最小值 如何利用call.apply来做继承 ...

  5. 理解JS中的call、apply、bind方法(*****************************************************************)

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向. call.apply.bind方法的共同点和区别:app ...

  6. js中call、apply、bind那些事2

    前言 回想起之前的一些面试,几乎每次都会问到一个js中关于call.apply.bind的问题,比如… 怎么利用call.apply来求一个数组中最大或者最小值 如何利用call.apply来做继承 ...

  7. js 之 call 、 apply

    在学习js过程中怎么也绕不过用到call.apply方法,感觉都差不多,现在看看他们的用法,区别 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(conte ...

  8. js简单 图片版时钟,带翻转效果

    js简单 图片版时钟,带翻转效果 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"& ...

  9. js简单操作Cookie

    贴一段js简单操作Cookie的代码: //获取指定名称的cookie的值 function getCookie(objName) { var arrStr = document.cookie.spl ...

随机推荐

  1. Word2010制作自动目录

    原文链接:https://www.toutiao.com/i6488296610873737741/ 原文从网上复制: 查看"开始"选项卡,"样式"功能组,我们 ...

  2. C++高并发场景下读多写少的解决方案

    C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...

  3. vscode搜索高亮个性化设置

    "workbench.colorCustomizations": { "editor.selectionHighlightBorder": "#1ED ...

  4. How to die?

    下次给一个 vector 搞 unique 之前,一定要记得给它排序!!1(少点自以为是) 对一个 set "同时"删除两个数时,一定要注意特判两个数是否重复/重叠. 有两个序列, ...

  5. Python 为什么不设计 do-while 循环结构?

    在某些编程语言中,例如 C/C++.C#.PHP.Java.JavaScript 等等,do-while 是一种基本的循环结构. 它的核心语义是:先执行一遍循环体代码,然后执行一遍条件语句,若条件语句 ...

  6. 设置图片DPI

    //image 可以先转换为 bitmap Bitmap bmp = new Bitmap(width, height); bmp.SetResolution(300, 300);

  7. 什么是Filter过滤器

    一,什么是Filter过滤器: JavaWeb三大组件之一 Filter过滤器是JavaEE的规范.也就是接口 Filter过滤器作用:拦截请求,过滤响应 拦截请求常见的应用场景有: 1,权限检查 2 ...

  8. 响应的HTTP协议介绍及常见响应码说明

    响应的HTTP协议介绍 1,响应行 1)响应的协议和版本号 HTTP/1.1 2)响应状态码 200 3)响应状态描述符 ok 2,响应头 1)key:value 不同的响应头,有其不同含义 空行 3 ...

  9. 使用AJAX请求调用出现HTTPS协议错误问题

    前言: 这又是一个可能是半路就卡机的项目,在调用ajax的时候遇到了下面的这个错. js中有个ajax请求http,url是:http//:.js就提示请求了一个不安全的脚本,在发送ajax请求时,就 ...

  10. 负载均衡的比例(权重,ip_hash,轮询)

    目录 一:负载均衡的比例 1.轮询 2.权重 3.ip_hash 二:测试轮询 1.测试 2.重启 3.网址测试 三:测试ip_hash 一:负载均衡的比例 1.轮询 # 默认情况下,Nginx负载均 ...