当前位置: 首页 > news >正文

手写实现call() apply() bind()函数,附有详细注释,包含this指向、arguments讲解

手写实现call() apply() bind()函数是很经典的问题,但是能掰扯清楚的文章确实不算多,于是笔者才决定写下本文,希望能给读者带来一些启发,如有错误欢迎指正。

目录

补充知识

函数中的this指向

 类数组对象arguments

call()

原理:

详细实现:

验证:

apply()

原理:

详细实现:

 验证:

bind()

原理:

详细实现:

验证:


补充知识

在往下看具体的实现之前,我们先要了解一些前置补充知识

函数中的this指向

函数中的this指向是在函数被调用的时候确定的,也就是执行上下文被创建时确定的。

在一个执行上下文中,this由调用者提供,调用函数的方式来决定。

1.方法调用模式

哪个对象调用函数(object.method()),this就指向哪个对象

let obj={a:1,b:2,
}
obj.test=function(){let a=3console.log(this,this.a)
}
obj.test()
//结果为 { a: 1, b: 2, test: [Function: test] } 1
//test的this指向obj

2.独立调用模式

独立调用时,指向window(严格模式下指向undefined)

// 例子1
window.a=111
var test1 =function(){let a=123console.log(this.a)
}
test1() // 输出结果为111,说明此时test1的this指向是全局的window// 例子2
window.a=111
var obj = {a:222
}obj.test2=function(){let a=333;console.log(this.a,'第一个')let func=function(){console.log(this.a,'第二个')}func()
}obj.test2()
// 输出结果为:
// ’222 第一个‘ 说明this指向是obj,因为test2是方法调用
// ‘111 第二个’ 说明this指向是window,因为func是独立调用

3.构造函数模式

js中,我们通过new关键词来调用构造函数,此时this会绑定在该实例对象

window.a=111
test3=function(){this.a=444
}
test3()
console.log(window.a) 
//结果为‘444’,说明this指向的是windows,因为上面test3的调用模式是独立调用let newObj= new test3()
console.log(newObj.a) 
// 结果为‘444’,说明this指向的是newObj,因为上面的test3的调用是作为构造函数的形式调用

 类数组对象arguments

arguments只在函数中存在(箭头函数除外)的伪数组(具有length,可以通过下标访问,但是不具有数组的方法, 比如push(),pop()等数组常用的方法),存储了我们传入的所有形参

例子

function testArgu(){console.log(arguments);
}
testArgu('a','b',123)

运行结果

我们可以看到,我们传的参数都在arguments中了 

call()

原理

当函数执行call方法的时候,实际上是把函数放到call()的第一个参数的某个属性上,然后再通过合格属性来执行函数

func.call(ctx,arg1,...)//等价于以下代码
ctx.fn=func;
ctx.fn(arg1,....)

详细实现

Function.prototype.myCall=function(context,...args){//对this进行类型判断,如果不是function类型,就报错//this应该指向的是调用myCall函数的对象(function也属于对象object类型)//因为myCall的调用模式是上文提到的‘方法调用模式’if(typeof this != 'function'){throw new TypeError('type error')}// 不传的话,默认上下文是windowvar context = context || window// 假如context上面有fn属性的时候,会出现冲突// 所以当context上面有fn属性的时候,要先保存一下var temp = nullif (context.fn) {temp = context.fn}// 给context创建一个fn属性,并将值设置为需要调用的函数(即this)context.fn = this//调用函数const res = context.fn(...args)// 删除context对象上的fn属性if (temp) {context.fn = temp} else {delete context.fn}// 返回运行结果return res
}

验证

let num=1;
let obj={num:2,fn:'this is obj.fn'
}function test(a,b,c,d){console.log(this.num,'test参数:',a,b,c,d)
}// 调用myCall函数
test.myCall(obj,4,3,2,1)// 检查obj本身的fn是否被修改
console.log(obj.fn)

以上验证代码运行结果为:

说明手写的myCall方法可以修改this指向,并且obj本身的fn未被修改

apply()

原理:

基本原理和call类似,区别就是对参数对处理不同:

call方法接受的参数是一个参数列表,而apply方法接受的是一个包含多个参数的数组。

这里我们就可以用到上文说的类数组对象arguments来处理参数了

详细实现:

// 和myCall的不同之处1:参数
Function.prototype.myApply=function(context){if(typeof this!== 'function'){throw new TypeError('type error')}var context = context || windowvar temp = nullif (context.fn) {temp = context.fn}context.fn = thislet res;// 和myCall的不同之处2:参数的处理// 判断第二个参数是否存在if (arguments[1]) {res = context.fn(...arguments[1])}else{res = context.fn()}// 删除context对象上的fn属性if (temp) {context.fn = temp} else {delete context.fn}// 返回运行结果return res
}

 验证:

let num=1;
let obj={num:2,fn:'this is obj.fn'
}function test(a,b,c,d){console.log(this.num,'test参数:',a,b,c,d)
}// 调用myCall函数
test.myApply(obj,[1,2,3,4])// 检查obj本身的fn是否被修改
console.log(obj.fn)

 运行结果为:

 

bind()

原理:

bind需要考虑一种情况,就是bind返回的函数作为构造函数使用的时候,bind绑定的this失效,但是参数依旧有效。

如何区分bind是正常使用还是当构造函数使用呢?根据this判断就行了。因为函数的this指向取决于如何调用(上文讲到过)。

1.当构造函数的时候,this指向新建的实例对象(此时this的prototype在该构造函数上)。

2.正常使用,this指向window

详细实现:

Function.prototype.myBind=function(context){if(typeof this!== 'function'){throw new TypeError('type error')}// 获取参数var args0 =[...arguments].slice(1);// 保存this,如果作为构造函数使用,此时this会指向实例fn=this;//返回更改this指向的函数return function Fn(...args){//如果是new的形式来使用绑定函数的if(this instanceof Fn) return new fn( ...args0,...args)//如果是普通函数的形式来使用绑定函数的else return fn.call(context, ...args0,...args);}
}

验证:

function Point(x, y) {this.x = x;this.y = y;
}// 情况1:正常调用bind函数
var testObj = {};
var YAxisPoint = Point.myBind(testObj, 0 );
YAxisPoint(1)
console.log(testObj)// 情况2:bind返回函数作为构造函数
// 此时之前绑定的指向testObj的this会失效,会重新指向新的对象实例,但是参数会继续有效
let newObj=new YAxisPoint(2);
console.log(newObj)

运行结果为

符合预期

 

相关文章:

手写实现call() apply() bind()函数,附有详细注释,包含this指向、arguments讲解

手写实现call() apply() bind()函数是很经典的问题,但是能掰扯清楚的文章确实不算多,于是笔者才决定写下本文,希望能给读者带来一些启发,如有错误欢迎指正。 目录 补充知识 函数中的this指向 类数组对象arguments call() 原理…...

MySQL中日期、时间直接相减的坑

前言 在牛客网上写一道 SQL 题时,需要计算两个日期之间相隔的秒数,我在写的时候直接将两个日期进行相减,得出来的值却不是相差的秒数。 情景再现 我在 MySQL 中进行了测试,得出的结论是:如果日期类型直接相减&#…...

漏洞发现-web应用发现探针类型利用(43)

关于在真实环境下面,这个漏洞该如何发现 这里老师把它分成了三块第一类是 #已知cms 如常见的dedecms,discuz,wordpress等源码结构,这些都是网上比较知名的php源码的cms的名称,这是我们在国内常见的几个程序&#xf…...

专门针对开发人员,攻击者利用Rust获取操作系统信息

近日,研究人员在 Rust 编程语言的 crate 注册表中发现了一些恶意软件包,专门针对开发人员。 Phylum 在上周发布的一份报告中称,这些库是由一个名为 "amaperf "的用户在 2023 年 8 月 14 日至 16 日之间上传的。现已删除的软件包名…...

PHP8的箭头函数-PHP8知识详解

php 7.4 引入了箭头函数(Arrow Functions),并在 PHP 8 中得到了进一步改进和扩展。 箭头函数是一种更简洁的匿名函数形式,它们提供了一种更便捷的方式来定义轻量级的、单行的回调函数。 箭头函数的语法如下: fn (参…...

初识PHP编程:探索Web开发的起点

初识PHP编程:探索Web开发的起点 PHP(Hypertext Preprocessor)是一种广泛使用的服务器端脚本语言,专门用于Web开发。它的强大功能和简单易学的语法使得它成为初学者和专业开发者的首选。在本文中,我们将探索什么是PHP&…...

Git——Windows平台创建gitee私有仓库详解

目录 1. 安装git 2. gitbash配置 2.1 设置 2.2 生成key 2.3 项目管理 2.3.1 本地新建 2.3.2 clone远程仓库的工程到本地改文件 1. 安装git 默认安装。 2. gitbash配置 2.1 设置 打开gitbash,设置用户名和邮箱: git config --global user.name …...

Git基础教程-常用命令整理:学会Git使用方法和错误解决

目录 一、了解Git的基本概念 二、Git的安装和配置 Git的安装 Git的配置 用户信息 文本编辑器 差异分析工具 查看配置信息 三、Git的基本操作 基本原理 基本操作命令 基本操作示例 场景一:创建新仓库 场景二:拉取并编辑远程仓库 四、常见问…...

Ops实践 | 国产化KylinOS系统中快速部署企业内部高性能DNS服务器、时间同步服务器 (精选)...

各位看友,由于微信公众号推送机制改变,现在需要设置为星标才能收到的本公众号推送消息哟。关注回复【学习交流群】加入【安全开发运维】答疑交流群 请朋友们【多多点击文中的广告】,支持作者更新更多文章。 目录: 本文为作者原创文章&#xf…...

stm32之IIC协议

主要通过两个层面来讲:物理层、协议层。 IIC是一个同步半双工串行总线协议。 一、物理层(通信模型) 1、最早是飞利浦公司开发的这个协议,最早应用到其产品上去。 2、两线制(两根信号线) 其中SCL为时钟…...

范式 事务 多表查询

范式 概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须遵循前边的所有范式要求 第一范式: 数据库表的每一列都是不可分割的基本数据项 这样子就不满足第一范式 这样子就满足第一范式 存在问题: 数…...

基于白鲸算法优化的BP神经网络(预测应用) - 附代码

基于白鲸算法优化的BP神经网络(预测应用) - 附代码 文章目录 基于白鲸算法优化的BP神经网络(预测应用) - 附代码1.数据介绍2.白鲸优化BP神经网络2.1 BP神经网络参数设置2.2 白鲸算法应用 4.测试结果:5.Matlab代码 摘要…...

java并发编程 ReentrantLock详解

文章目录 1 概要2 相关文章3 例子4 方法详解4.1 lock()4.2 unlock()4.3 tryLock()4.4 其他公平锁 总结 1 概要 ReentrantLock 通过实现Lock接口的行为,提供锁机制。但是实现委托给了内部的Sync,Sync extends AbstractQueuedSynchronizer,继承…...

Java获取文件内容IO流

文章目录 InputStream和ReaderScannerNIO外传 一般读取文件类的使用字符流即可 InputStream和Reader InputStream和Reader是Java IO中的两个重要的抽象基类,InputStream是二进制流,Reader是字符流。使用InputStream或者Reader读取文件内容可以帮助我们…...

Java后端开发面试题——集合篇

ArrayList底层的实现原理是什么 底层数据结构 ArrayList底层是用动态的数组实现的 初始容量 ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10 扩容逻辑 ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组 添加逻…...

如何允许远程访问MySQL

许多网站和应用程序一开始都将web服务器和数据库后端托管在同一台机器上。不过,随着时间的推移,这样的设置可能会变得繁琐和难以扩展。一种常见的解决方案是通过设置远程数据库来分离这些功能,允许服务器和数据库在各自的机器上按自己的速度增…...

001图机器学习与图神经网络简介

文章目录 一. 无处不在的图二. 如何对图数据做信息挖掘三. 图神经网络四. 图机器学习常用的编程工具五. 图的可视化工具六. 常见的图数据库七. 图机器学习的应用举例八. 结束语 一. 无处不在的图 一切具有关联关系的数据都可以用图来表示。比如:交通网、知识图谱、…...

万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录 前言.万级数据优化一. 直接上流式查询封装工具代码二. 传统分页导出查询三. 流式查询概念游标查询 前言.万级数据优化 我们不妨先给大家讲一个概念,利用此概念我们正好给大家介绍一个数据库优化的小技巧: 需求如下:将一个地市表的数…...

Unity——脚本序列化

在介绍序列化之前,我们先来了解一下为什么要对数据进行序列化 数据序列化有以下几个主要的应用场景和目的: 1. 持久化存储:序列化可以将对象或数据结构转换为字节序列,使得其可以被存储在磁盘上或数据库中。通过序列化&#xff…...

es(Elasticsearch)介绍

学习es可以参考mysql(相比mysql而言,es所需的cpu、内存更多) 什么是Elasticsearch Elasticsearch简称es,是由Elastic和search组成。Elastic的意思是有弹性的,search的意思是搜索。 弹性:es是一个天生支持分…...

C++中使用 do…while 循环

C中使用 do…while 循环 在有些情况(如程序清单 6.8 所示的情况)下,您需要将代码放在循环中,并确保它们至少执行一次。此时 do…while 循环可派上用场。 do…while 循环的语法如下: do {StatementBlock; // executed…...

开源vue动态表单组件

一、项目简介 vueelement的动态表单组件,拖拽组件到面板即可实现一个表单 二、实现功能 支持拖拽 支持输入框 支持文本框 支持数字输入框 支持下拉选择器 支持多选框 支持日期控件 支持开关 支持动态表格 支持上传图片 支持上传文件 支持标签 支持ht…...

怎么从0到1创建一个PHP框架-1?

写在前面 本人开发的框架在2021年年初开发完成,后面没有再做过任何维护和修改。是仅供大家参考交流的学习项目,请勿使用在生产环境,也勿用作商业用途。 框架地址: https://github.com/yijiebaiyi/fast_framework 整体思路 开发…...

Qt无边框青绿色主题

收费产品,学生党、闹眼子党勿扰 收费金额:500元 1 概述 最近因项目需要,写了一个炫酷的青绿色、无边框界面,和3DSMax的界面有点类似。 2 截图 首先看看3DSMax的界面 不知道大家看出来没,这个ui其实很简单&#xff…...

200 套基于Java开发的Java毕业设计实战项目(含源码+说明文档)

文章目录 简介前言第一部分第二部分部分截图源码咨询 简介 博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 前言 对于java方向的毕业设计题目选题&#xf…...

Ansible学习笔记7

user模块: user模块用于管理用户账户和用户属性。 如果是windows要换一个win_user模块。 创建用户:present: [rootlocalhost ~]# ansible group1 -m user -a "nameaaa statepresent" 192.168.17.106 | CHANGED > {"ansi…...

Python3 对列表、字典以及二者的嵌套数据(JSON)格式排序

在 Python 中,列表和字典都是基础数据类型,这两种数据类型会通过相互嵌套和多个层级形成复杂的数据类型,类似 JSON 数据格式,对列表和字典排序其实可以类比是对 JSON 排序。 列表排序 列表可以使用 sorted() 函数排序&#xff1…...

如何在B站进行学习直播

诸神缄默不语-个人CSDN博文目录 会根据我使用的情况进行持续更新 文章目录 1. 电脑 - 哔哩哔哩直播姬1. 软件的基础使用2. 素材1. 摄像头2. 窗口捕捉3. 游戏进程图片文字浏览器多媒体 3. H5插件其他注意事项 2. 手机直播3. iPad直播 1. 电脑 - 哔哩哔哩直播姬 1. 软件的基础使…...

老卫带你学---windows上安装minikube

老卫带你学—windows上安装minikube 1. 下载minikube https://storage.googleapis.com/minikube/releases/latest/minikube-installer.exe2.安装好后,将对应的目录添加env path 3. minikube start --kubernetes-versionv1.23.8 --image-mirror-countrycn...

Neo-reGeorg隧道搭建

目录 Neo-regeorg前言 环境搭建 具体使用 kail安装Neo-reGeorg kail内生成webshell并设置密码 kail与win10连接 windows server内打开服务 kail虚拟机访问windows server以及所在的内网 Neo-regeorg前言 regeorg为reDuh的升级版,主要功能就是把内网服务器的…...