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

码上【call,apply,bind】的手写

一、call
(1)官方用法

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

语法:function.call(要绑定的this值,参数,参数,…)。不一定这些参数都需要,这些参数都是可选的,返回值:使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

// 不指定参数
var nickname = 'global'
​
function getName() {console.log(this.nickname);
}
getName() //global
getName.call() //global 

在严格模式下,this 的值将会是 undefined。当不给call指定参数时,相当于将不改变this的指向,在哪调用的就指向哪儿,即没有传递第一个参数,this的值将会被绑定为全局对象。

// 指定第一个参数
var name = 'cat'
var obj = {name: 'dog'
}
​
function getName() {console.log(this.name);
}
getName() //cat
getName.call(obj) //dog
console.log(obj)//{ name: 'dog'} 

当指定第一个参数时,第一个参数代表新的this值,当调用方法时,该方法的this值会绑定到obj对象,于是会访问这个对象的name属性。

//指定第一个参数和其他参数
var obj = {user: 'Ducky',fn: function(a, b) {console.log(a + b);console.log(this.user);}
}
var b = obj.fn;
b.call(obj, 1, 2);
// 3
// Ducky 

除了第一个参数,其余参数都是用来做一些必要的运算等。运行到这,应该不难发现,使用Function.call()的时候,函数是谁执行的?是使用call绑定对象的之后,call也把函数执行了。

相信你已经会用了,试着写写实现原理。

(2)实现原理

实现call的关键在于:

第一:如何给函数绑定新的this

第二:如何在绑定完this后把函数也给执行完毕?

掌握了这两个关键,那么一切都有迹可循了。接下来看看换成自己手写的是不是一样的效果:

Function.prototype.my_call = function(context) {// 如何实现绑定新的thiscontext.fn = this //context['fn']=this// 如何在调用call时把调用call的函数也执行context.fn()
} 

是不是大吃一惊?就实现了?是的,就是实现了,核心原理就是这两个关键,不信可以测试一下:

var name = 'cat'
var obj = {name: 'dog'
}
​
function getName() {console.log(this.name);
}
getName() //cat
getName.my_call(obj) //dog
console.log(obj)//{ name: 'dog', fn: [Function: getName] } 

真的绑定成功了!接下来继续做点优化,优化也有几个关键点:

第一:非得是函数才可以调用call

第二:call除了接收第一个参数(新的this)外,还可以接收一个参数列表;

第三:官方call调用后,不会改变新的thisobj)的结构,在上述代码中obj内部新增了一个属性fn

第四:当call不传第一个参数时,需指向全局对象(window);

第五:调用call时,需得有返回值;

第六:当将this挂载到新的this上时,后者已经存在该属性的情况下,还是会改变后者的结构。

终极实现原理:

Function.prototype.my_call = function(context, ...args) {if (typeof this !== 'function') throw new TypeError('error')context = context || 'window'let fn = Symbol('fn')context[fn] = this //context.fn = this const res = context[fn](...args)delete context[fn]return res
} 

虽然考虑了六种情况,但是代码还是很可人,这回,它就是与官方的源码一样的效果和功能了,简直不要太完美~

二、apply
(1)官方用法

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。

语法:function.apply(要绑定的this的值,[参数,参数,参数,…]),返回值:调用有指定 this 值和参数的函数的结果。

可见,它跟call就只有一个区别:提供参数的方式不同。apply 使用参数数组而不是一组参数列表。apply 可以使用数组字面量(array literal),如 fun.apply(this, ['dog', 'cat']),或数组对象,如 fun.apply(this, new Array('dog', 'cat')),还可以使用 arguments 对象作为 argsArray 参数。arguments 是一个函数的局部变量。它可以被用作被调用对象的所有未指定的参数。这样,在使用 apply 函数的时候就不需要知道被调用对象的所有参数及其个数。可以直接使用 arguments 来把所有的参数传递给被调用对象。被调用对象接下来就负责处理这些参数,要解构,要切割,要指定索引都可以。

既然apply的用法和call几乎相同,只是除第一个参数外,其余参数传递方式不一样,那么我们直接在my_call的手写上修改传递参数的形式即可。

(2)实现原理
Function.prototype.my_apply = function(context, args) {if (typeof this !== 'function') throw new TypeError('error')context = context || 'window'let fn = Symbol('fn')context[fn] = this //context.fn = this const res = context[fn](...args)delete context[fn]return res
} 

这。。。。,还是测试一下吧:

var obj = {user: 'Ducky',fn: function(a, b) {console.log(a + b);console.log(this.user);}
}
var b = obj.fn;
b.my_apply(obj, [1, 2]);
// 3
// Ducky 

好了,成功了,该考虑的已经在手写call的时候考虑过了,就是这么的干净利落。

三、bind
(1)官方用法

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,提供调用时使用。

语法:function.bind(要绑定的this值,参数1,参数2,参数3)。

这里说明,第一个参数是如果为空,或者为null||undefined,执行作用域的this将被视为形函数的的this值,其余参数(当目标函数被调用时,被预置入绑定函数的参数列表中的参数)可有可无,也可在返回的新函数里面传递。返回值是返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

举个例子:

var name = 'cat'
var obj = {name: 'dog',fn: foo
}
function foo(a, b) {console.log(this.name, a + b);return a + b
}
const bar = foo.bind(obj)
bar(3, 4)
//dog 7 
(2)实现原理

从上述例子可以看出,bind()会创建一个新的绑定函数bar

实现bind函数的关键在于:

第一:如何新建一个绑定函数;

第二:如何将拿到在call()传递的参数和调用新建的绑定函数传递的参数并结合

第三:如果使用new操作符操作新建的那个绑定函数,this又该如何指向

先讨论以下前两个关键,新建一个绑定函数直接在call内部返回一个函数即可,将两个地方的传递的参数都传递给执行绑定函数。

Function.prototype.my_bind = function(context, ...args1) {// 保存外部函数的thisconst _this = thisreturn function bound(...args2) {//返回一个新函数//返回值return _this.call(context, ...args1, ...args2)}
} 

不要惊讶在实现bind函数内部用的是call||apply,大不了用刚刚手写的my_call||my_apply。大体上就实现了,测试一下:

var name = 'cat'
var obj = {name: 'dog',fn: foo
}
​
function foo(a, b) {console.log(this.name, a + b);return a + b
}
const bar = foo.my_bind(obj)
bar(3, 4)
//dog 7 

再来考虑第三个问题:

//官方bind
const bar = foo.bind(obj)
new bar(3, 4)//cat 7
​
//手写的my_bing
const bar = foo.my_bind(obj)
new bar(3, 4) //dog 7 

从上可以看出,当使用new操作符操作新的函数bar时,官方的bind会忽略绑定的this值,但是前置参数依然会提供给铭记函数,而我们手写的my_bind原封不动,这肯定得改!

现在无非是,当使用new运算符构造新建的绑定函数bar时,foo.bind(obj)中,foo的·this指向不指向obj,也不指向全局对象,而是会指向实例对象new bar()的执行作用域,接下来可以理一下思路:

//目标:(new bar)._proto_==foo.prototype
//即如果新建的绑定函数被new,bind的调用函数就会变成实力对象的构造函数
​
//接下来的操作都是在my_bind函数内部操作
//借助一个辅助函数
const help=function(){}
bound.prototype=new help()//继承到了foo(bind的调用函数)的原型
if(this.prototype){//this指的是bind的调用函数help.prototype=this.prototype}
​ 

通过一通操作,得到new help()._proto_==help.prototype=this.prototype==bound.prototypethis是什么,取决于调用bind函数的函数是什么,在这里是foo,所以new bound()._proto_==bound.prototype=foo.prototype,那么,调用bind函数的this到底指向什么取决于,新建的绑定函数bar有没有被new,如果没有,则看传递的第一个参数,第一个参数为空则为全局对象,不为空则为指定的对象;如果被new了,那么调用nind函数的this指向新建的绑定函数的执行作用域,bind最终实现方式是:

Function.prototype.my_bind = function(context, ...args1) {if (typeof this !== 'function') {throw new TypeError('error')}context = context || windowconst _this = thisconst help = function() {}if (this.prototype) {help.prototype = this.prototype}const bound = function(...args2) {return _this.call(this instanceof help ? this : context,...args1,...args2)}bound.prototype = new help()return bound
} 

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

相关文章:

码上【call,apply,bind】的手写

一、call (1)官方用法 call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 语法:function.call(要绑定的this值,参数,参数,…)。不一定这些参数都需要,这些参数都…...

代谢组学Nature子刊!抑郁症居然“男女有别”,脑膜淋巴管起关键作用!

文章标题:A functional role of meningeal lymphatics in sex difference of stress susceptibility in mice 发表期刊:Nature Communications 影响因子:17.694 发表时间:2022年8月 作者单位:中山大学中山医学院 …...

nacos配置中心搭建

网站每次更新版本都有短暂暂停,影响用户使用,返回经常不可用,需要改进 需要实现高可用,搭建负载均衡,实现jenkinsnacos不停机部署 nacos搭建预备环境准备 64 bit OS,支持 Linux/Unix/Mac/Windows&#x…...

uni-app低成本封装一个取色器组件

在uni-ui中找不到对应的工具 后面想想也是 移动端取色干什么&#xff1f; 没办法 也挂不住特殊需求 因为去应用市场下载 这总东西 又不是很有必要 那么 下面这个组件或许能解决您的烦恼 <template><view class"content"><view class"dialog&…...

APP 怎么免费接入 MobPush

1、获取 AppKey 申请 Appkey 的流程&#xff0c;请点击 http://bbs.mob.com/thread-8212-1-1.html?fromuid70819 2、下载 SDK 下载解压后&#xff0c;如下图&#xff1a; 目录结构 &#xff08;1&#xff09;Sample&#xff1a;演示Demo。&#xff08;2&#xff09;SDK&am…...

XGBoost

目录 1.XGBoost推导示意图 2.分裂节点算法 Weighted Quantile Sketch 3.对缺失值得处理 1.XGBoost推导示意图 XGBoost有两个很不错得典型算法&#xff0c;分别是用来进行分裂节点选择和缺失值处理 2.分裂节点算法 Weighted Quantile Sketch 对于特征切点点得选择&#xff…...

你是什么时候从轻视到高看软件测试的?

刚开始学软件测试很轻视&#xff0c;因为我那时很无知&#xff0c;这也是那时绝大多数人员的心态&#xff0c;那时中国最讲究“编程才是硬道理”。 如今却非常热爱软件测试&#xff0c;包括软件测试工具&#xff0c;方法&#xff0c;理论&#xff0c;技术。因为我在3年的测试工…...

基于ssm的航空售票系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经从做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xf…...

滑动窗口最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。 示例: 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 …...

接口文档参考示例

接口文档参考示例 用户登录 - POST /api/login/ 接口说明:登录成功后,会生成或更新用户令牌(token)。 使用帮助:测试数据库中预设了四个可供使用的账号,如下表所示。 Untitled 请求参数: Untitled 响应信息: 登录成功: {"code": 30000, "message&qu…...

2010-2019年290个城市经济发展与环境污染数据

2010-2019年290个城市经济发展与环境污染数据 1、时间&#xff1a;2010-2019年 2、统计口径&#xff1a;全市 3、来源&#xff1a;城市统计NJ&#xff0c;缺失情况与年鉴一致 4、指标包括&#xff1a; 综合经济&#xff1a;地区生产总值、人均地区生产总值、地区生产总值增…...

web开发

目录 使用Idea搭建Web项目 使用Idea开发Web项目基本知识 tomcat配置信息 HTML /CSS 开发主页 Servlet 学习和掌握的内容&#xff1a; HTML/CSSServlet MVC模式和Web开发数据库基本应用和JDBC应用软件项目开发流程 环境及工具版本&#xff1a; Windows10,JDK1.8 Idea2…...

【数据结构】优先级队列----堆

优先级队列----堆优先级队列堆堆的创建堆的插入&#xff1a;堆的删除&#xff1a;PriorityQueue的特性PriorityQueue的构造与方法优先级队列 优先级队列&#xff1a; 不同于先进先出的普通队列&#xff0c;在一些情况下&#xff0c;优先级高的元素要先出队列。而这种队列需要提…...

Python深度学习实战PyQt5信号与槽的连接

本文讲解信号与槽的连接机制&#xff0c;详细示范各种类型的信号/槽连接的实现方法&#xff0c;这是图形用户界面的核心内容。还将介绍面向对象的程序设计&#xff0c;这是图形用户界面的基本思想目录1. 信号与槽&#xff08;Signals and slots&#xff09;信号与槽机制是 PyQt…...

Window 10 OpenCV 打开罗技(Logitech)摄像头速度慢问题解决

采用最新版OpenCV 4.7.0 摄像头对罗技摄像头进行视频图像抓取时&#xff0c;发现存在打开摄像头慢的问题。 测试环境如下&#xff1a; 系统Windows 10 专业版CPUIntel i7-7700K 4.20GHz 摄像头型号罗技Logitech C930c 网络摄像头OpenCV版本4.7.0语言C 测试结果表明&#xff…...

基于yolo的小球位置实时检测

基于yolo的小球位置实时检测 Yolo安装 操作系统&#xff1a;ubuntu 安装cuda和opencv git clone https://github.com/pjreddie/darknet.git cd darknet 修改Makefile文件&#xff0c;使GPU1&#xff0c;OPENCV1 make 2. 数据集处理 2.1 制作数据集 将小球放在摄像头前…...

【微服务】Elasticsearch数据聚合自动补全数据同步(四)

&#x1f697;Es学习第四站~ &#x1f6a9;Es学习起始站&#xff1a;【微服务】Elasticsearch概述&环境搭建(一) &#x1f6a9;本文已收录至专栏&#xff1a;微服务探索之旅 &#x1f44d;希望您能有所收获 在第二站的学习中&#xff0c;我们已经导入了大量数据到es中&…...

java面试题(十七)spring

2.1 请你说说Spring的核心是什么 参考答案 Spring框架包含众多模块&#xff0c;如Core、Testing、Data Access、Web Servlet等&#xff0c;其中Core是整个Spring框架的核心模块。Core模块提供了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能&#xff0c;而这些功能…...

你知道 BI 是什么吗?关于 BI 系统的概述

BI 作为信息化建设中的关键一环&#xff0c;在企业中通常起到承上启下的作用&#xff0c;下能连接打通企业业务系统数据库&#xff0c;将各部门数据分类分级统一储存到数据仓库&#xff0c;简化存储取数流程&#xff0c;减少人力、时间成本&#xff1b;上能提供数据可视化报表…...

git:详解git rebase命令

背景 今天无意中打开 git 官网&#xff0c;发现 git 命令还是很多的&#xff0c;然而我们常用的就那几个&#xff0c;今天来学习一个也不怎么常用的命令 rebase 官网链接 都说学一个东西最好的方式就是读他的 官方文档&#xff0c;这里我读了一遍&#xff0c;把一些核心的地…...

第四章——随机变量的数字特征

文章目录1、数字特征的定义2、数学期望&#xff08;均值&#xff09;2.1、数学期望的定义及性质2.1.1、定义2.1.2、性质2.2、数学期望相关例题2.3、Yg(X)的数学期望2.4、Zg(X,Y)的数学期望2.5、随机变量函数的数学期望例题3、方差3.1、方差的定义与性质3.2、相关例题3.3、切比雪…...

vue2源码阅读理解-响应式数据原理

首先明确&#xff0c;vue2是如何实现响应式的&#xff1f; 通过object.defineProperty观察者模式实现&#xff0c;在创建vue实例的过程中&#xff0c;也就是介于beforecomputed~computed的过程中&#xff0c;会执行如下函数initState export function initState (vm: Componen…...

服务调用分布式session

目录一、nginx动静分离二、服务调用1、创建配置zmall-cart购物车模块2、创建配置zmall-order订单模块3、服务调用三、spring session实战1、什么是Spring Session2、为什么要使用Spring Session3、错误案例展示4、配置spring-session四、二级域名问题五、用户登录一、nginx动静…...

Maven知识点-插件-maven-surefire-plugin简介

Maven本身并不是一个单元测试框架&#xff0c;Java 世界中主流的单元测试框架为JUnit 和TestNG。 Maven 所做的只是在构建执行到特定生命周期阶段的时候&#xff0c;通过插件来执行JUnit或者TestNG的测试用例。 这一插件就是maven-surefire-plugin&#xff0c;可以称之为测试…...

如何借力Alluxio推动大数据产品性能提升与成本优化?

内容简介 随着数字化不断发展&#xff0c;各行各业数据呈现海量增长的趋势。存算分离将存储系统和计算框架拆分为独立的模块&#xff0c;Alluxio作为如今主流云数据编排软件之一&#xff0c;为计算型应用&#xff08;如 Apache Spark、Presto&#xff09;和存储系统&#xff0…...

linux shell脚本被包含是什么意思?.命令和source命令(在脚本中运行脚本,脚本中调用脚本)(脚本包含,父子脚本)

在 shell 编程中&#xff0c;当一个 shell 脚本被另一个 shell 脚本包含&#xff0c;即用 . 或 source 命令包含&#xff0c;则被包含的脚本在当前 shell 进程内执行&#xff0c;并且可以访问当前 shell 进程的环境变量和函数。 此时&#xff0c;$0 代表的是主脚本的名称&#…...

MySQL进阶篇之锁(lock)

05、锁 5.1、概述 1、介绍 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据…...

TMDSEVM6657LS评估板恢复出厂默认状态

TMDSEVM6657LS评估板恢复出厂默认状态 前言 TMDSEVM6657LS评估板特别适用于DSP开发的初学者&#xff0c;但有时候拿到手的开发板几经流转&#xff0c;被别人修改过&#xff0c;也可能自己烧录过程出错&#xff0c;导致开发板的状态未知等原因&#xff0c;需要恢复到出厂默认状…...

聊一聊,我对DDD的关键理解

作者&#xff1a;闵大为 阿里业务平台解决方案团队 当我们在学习DDD的过程中&#xff0c;感觉学而不得的时候&#xff0c;可能会问&#xff1a;我们还要学么&#xff1f;这的确引人深思。本文基于工作经验&#xff0c;尝试谈谈对DDD的一些理解。 一、序 《阿甘正传》中&#xf…...

算法笔记(一)—— 认识复杂度和简单排序算法

时间复杂度是在一个算法流程中&#xff0c;常数操作的数量级指标。&#xff08;最差情况下的算法表现&#xff09; 比较两个算法的优劣&#xff0c;在足够的空间下&#xff0c;看时间复杂度指标&#xff0c;若相同&#xff0c;需要在大数据运行下来判断两个算法的“常数项指标…...

衡水哪儿专业做网站/搜索风云榜百度

test test 是正则表达式的方法&#xff0c;参数是字符串&#xff0c;返回的是布尔值&#xff08;true或false&#xff09;&#xff0c;查找对应的字符串是否存在 exec RegExpObject.exec(string) exec是正则表达式的方法&#xff0c;它的参数是字符串&#xff0c;查找并返回当…...

如何登录网站备案/友情链接怎么购买

转自&#xff1a;http://blog.csdn.net/stormbjm/article/details/9086163 1、添加用户&#xff0c;首先用adduser命令添加一个普通用户&#xff0c;命令如下&#xff1a; #adduser tommy //添加一个名为tommy的用户#passwd tommy //修改密码 Changing password for user tom…...

做网站的费用计入哪个科目/深圳搜索引擎优化推广

09年接触Asp.Net编程&#xff0c;在10年开始接触三层&#xff0c;当时不懂架构更不了解三层的内在&#xff0c;只是知道通过三层产生的BLL、DAL、Model能够实现程序的方便管理&#xff0c;同时也因为代码生成器的帮忙&#xff0c;大大加快了开发的效率&#xff0c;我们可以用很…...

wordpress块引用美化/百度关键词排名怎么查

在阅读本文之前,你应该阅读过的系列: 《Flink重点难点:时间、窗口和流Join》 《Flink重点难点:网络流控和反压》 《Flink重点难点:维表关联理论和Join实战》 《Flink重点难点:内存模型与内存结构》 《Flink重点难点:Flink Table&SQL必知必会(一)》 Flink重点难点:F…...

阿里云ecs搭建web网站/电商运营主要做什么

Effective C 笔记二 构造/析构/赋值运算 条款05&#xff1a;了解C默默编写并调用哪些函数 编译器默认声明一个default构造函数、一个copy构造函数、一个copy assignment操作符和一个析构函数。这些函数都是public且inline。 1 class Empty { 2 public: 3 Empty() {...} 4 …...

开设网站需要什么/seo关键词快速提升软件官网

异步日志 log4j2最大的特点就是异步日志&#xff0c;其性能的提升主要也是从异步日志中受益&#xff0c;我们来看看如何使用log4j2的异步日志。 同步日志 异步日志 Log4j2提供了两种实现日志的方式&#xff0c;一个是通过AsyncAppender&#xff0c;一个是通过AsyncLogger&am…...