手写简单call,apply,bind
分析一下call的使用方法:call是显示绑定this指向,然后第一个参数是你所指向的this对象,后面跟着多个参数,以逗号隔开
function sum(num1,num2){
return num1 + num2
}
sum.call({},1,2) // 3
上面是一个最简单的call使用方法,call的第一个参数,可以是对象,可以是数字,可以是空对象
当call的第一个参数是undefined或者是null时,他的指向是window
接下来就一步步的开始手写一个简单的call
首先第一步,我们需要在Function内添加一个原型对象hycall
Function.prototype.hycall=function(){
console.log('我是新的hycall对象');
}
sum.hycall() // 我是新的hycall对象
然后我们需要在内部执行sum的函数,但是由于这个hycall是通用式,不能写死内部的函数调用
这时候会有个问题:如何知道是谁调用了hycall??
其实很清楚的一个事情,对于this指向熟悉的都知道,sum.hycall是一个隐式绑定,此时的hycall内部的this就是当前的sum对象,我们用一个参数去接收this,再将其运行即可
Function.prototype.hycall=function(thisArg){
var fn = this // 外部的对象 sum,或者{}等等
fn()
}
function foo(){
console.log('我是foo对象',this);
}
sum.hycall({})
这里实现了一个最简单的call调用方式,接下来第二步,call的第一个参数是存在this指向的,此时的hycall需要接收一个参数对象thisArg,又因为这个参数是this的指向
问题二:如何让传来的this指向绑定到对象上?
对于传来的this指向,可以给其本身添加一个函数对象,再让其自身运行,相当于间接性的调用了函数
Function.prototype.hycall=function(thisArg){
var fn = this // 外部的对象 sum,或者{}等等
thisArg.fn=fn // thisArg身上添加一个fn
thisArg.fn() // 再将其调用即可
delete thisArg.fn // 调用完成之后再将其删除即可
}
function foo(){
console.log('我是foo对象',this);
}
sum.hycall({})
这里看似完成了对this参数的调用,但是此处又有问题出现:call是可以传入number等参数进入的,但是代码中不可能给一个数字添加函数例如123.fn(),此处又该如何解决?
这里有一个例子,当123要变成对象类型时,只需要new Number(123) ,便成为了一个对象。
以上例子说明,我们可以对thisArg进行对象转化
thisArg=Object(thisArg)
再考虑到call传入undefined或者null的时候,指向的this是window
我们对thisArg做出判断
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window
第三步,对于传值,call除了能传递this指向,后方还可以跟多个传值,对于我们的hycall的接收,此处可以使用es6的rest参数语法 ...
Function.prototype.hycall=function(thisArg,...args){
var fn = this // 外部的对象 sum,或者{}等等
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window // 判断thisArg是否存在,存在将其转换,不存在让其指向window
thisArg.fn=fn // thisArg身上添加一个fn
thisArg.fn(...args) // 再将其调用即可 spread--展开运算符
delete thisArg.fn // 调用完成之后再将其删除即可
}
function sum(a,b){
return a+b
}
sum.hycall({},1,2)
最后一步,接收返回值并返回出去
Function.prototype.hycall=function(thisArg,...args){
var fn = this // 外部的对象 sum,或者{}等等
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window // 判断thisArg是否存在,存在将其转换,不存在让其指向window
thisArg.fn=fn // thisArg身上添加一个fn
var result = thisArg.fn(...args) // 再将其调用即可 spread--展开运算符
delete thisArg.fn // 调用完成之后再将其删除即可
return result
}
function sum(a,b){
return a+b
}
sum.hycall({},1,2)
以上是对call的一个简单的手写,如果对call的手写熟悉了,apply和bind其实也大同小异,apply第一个参数和call一样,但是第二个参数是以数组的方式进行传递
function sum(num1,num2){
console.log("sum被调用",this,num1,num2);
return num1+num2
}
// 系统调用
var result=sum.apply("abc",[20,30])
console.log(result);
上方是一个正常的系统调用,apply第二个参数不传值时会和call有些不一样
Function.prototype.hyapply=function(thisArg,argArray){
var fn = this // 外部的对象 sum,或者{}等等
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window // 判断thisArg是否存在,存在将其转换,不存在让其指向window
argArray=argArray || []
thisArg.fn=fn // thisArg身上添加一个fn
var result = thisArg.fn(...argArray) // 再将其调用即可 spread--展开运算符
delete thisArg.fn // 调用完成之后再将其删除即可
return result
}
function sum(a,b){
return a+b
}
sum.hyapply({},1,2)
这里又有人要提问了:为什么call那里就可以不用判断第二个参数的值,你这里就需要呢?
因为在hycall中的第二个参数是...args 倘若我们未传值过来,...args的值=[],所以不需要对call的第二参数做出判断
下面开始对bind进行分析,bind是以返回一个函数的方法进行使用的
function sum(num1,num2,num3,num4){
console.log(num1,num2,num3.num4)
}
function bar(){
console.log("我是bar")
}
// 方式一
var newBar=bar.bind("aaa")
newBar()
// 方式二
var newSum=sum.bind("aaa",10,20,30,40)
newSum()
// 方式三
var newSum=sum.bind("aaa",10)
newSum(20,30,40)
手写bind函数
Function.prototype.hybind=function(thisArg,...argArray){
var fn = this // 外部的对象 sum,或者{}等等
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window
function proxyFn(){
thisArg.fn=fn
var result=thisArg.fn()
deletr thisArg.fn
return result
}
return proxyFn
}
function sum(a,b){
return a+b
}
var newSum=sum.hybind("aaa")
newSum()
接下来需要考虑上方的一种情况
// 方式三
var newSum=sum.bind("aaa",10)
newSum(20,30,40)
由于我们的hybind只有两个参数,但你传第三个参数的时候,就需要内部调用
Function.prototype.hybind=function(thisArg,...argArray){
var fn = this // 外部的对象 sum,或者{}等等
thisArg=(thisArg !== null && thisArg !== undefined) ? thisArg=Object(thisArg) : window
function proxyFn(...args){ //此处接收第三个参数
thisArg.fn=fn
var finalArgs=[...argArray,...args]//拼接两个数组
var result=thisArg.fn(...finalArgs)
deletr thisArg.fn
return result
}
return proxyFn
}
function sum(a,b){
return a+b
}
var newSum=sum.hybind("aaa",10)
newSum(20,30,40)
以上就是对三个显示绑定的简单手写,其实对于上面的边界判断其实还不是很完善,例如=0的时候(优化边界判断),例如fn本身就存在然后给覆盖(使用ES6-symbol语法定义解决)等等
如有更好的意见和完善的地方欢迎补充留言,感谢coderwhy老师的指导
手写简单call,apply,bind的更多相关文章
- 不使用Tomcat,手写简单的web服务
背景: 公司使用的YDB提供了http的查询数据库服务,直接通过url传入sql语句查询数据-_-||.ydb的使用参照:https://www.cnblogs.com/hd-zg/p/7115112 ...
- 手写call、apply、bind
区别&联系 三者都是指定函数执行时的上下文,第一个参数都是上下文: call从第二个参数开始,后续所有的参数传递给函数执行: apply第二个参数是一个数组,传递给函数执行: bind返回一个 ...
- 手写简单的jq雪花飘落
闲来无事,准备写个雪花飘落的效果,没有写太牛逼的特效,极大的简化了代码量,这样容易读取代码,用起来也很简单,对于那些小白简直是福利啊,简单易读易学.先直接上代码吧,然后再一一讲解,直接复制粘贴就可以拿 ...
- 利用Java手写简单的httpserver
前言: 在看完尚学堂JAVA300中讲解如何实现一个最简单的httpserver部分的视频之后, 一.前置知识 1.HTTP协议 当前互联网网页访问主要采用了B/S的模式,既一个浏览器,一个服务器,浏 ...
- 手写简单PE
环境工具:Windows 10 010Editor 目标程序功能: 调用MessageBoxA弹出消息框. 1.构造DOS头 typedef struct _IMAGE_DOS_HEADER { // ...
- 通过反射来手写简单的ORM SQlserver
不说废话,直接上干货,如发现问题,欢迎大家指出,谢谢! //------------------------------------MySQlServerORM [简单 CURD] using Sys ...
- 手写简单的promise
function Promise(fn) { var that = this; this.status = "pedding"; this.value = undefined; / ...
- node搭环境(四)--webpack启服务运行VUE模块文件(手写简单脚手架)
webpack启服务步骤: 1.新建空文件夹webpack-vue.在空文件夹右键点击- GIt Bath here--输入cnpm init--按程序走完会生成package.json文件 2.打开 ...
- 手写简单的php生成Html网页
这个是基本功,以前用到laravel及thinkphp时,这一步,都被设置好了吧. 这里只依靠纯的php环境,而没有任何框架, 而框架,只是将这一切规范化,加快代码效率及减小沟通成本,维护升级也方便, ...
随机推荐
- 面试造火箭系列,栽在了cglib和jdk动态代理
"喂,你好,我是XX巴巴公司的技术面试官,请问你是张小帅吗".声音是从电话那头传来的 "是的,你好".小帅暗喜,大厂终于找上我了. "下面我们来进行一 ...
- 「算法笔记」Link-Cut Tree
一.简介 Link-Cut Tree (简称 LCT) 是一种用来维护动态森林连通性的数据结构,适用于动态树问题. 类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护, ...
- Unsupervised Domain Adaptation by Backpropagation
目录 概 主要内容 代码 Ganin Y. and Lempitsky V. Unsupervised Domain Adaptation by Backpropagation. ICML 2015. ...
- Vue.js高效前端开发 • 【Vue列表渲染】
全部章节 >>>> 文章目录 一.v-for指令 1.v-for指令使用 2.实践练习(待更新) 二.计算属性 1.计算属性创建和使用 2.实践练习(待更新) 三.侦听属性 1 ...
- VUE3 之 click 事件
1. 概述 老话说的好:努力帮别人解决难题,你的难题也就不难解决了. 言归正传,今天我们来聊聊 VUE3 的 click 事件的相关知识. 2. click 事件 2.1 实现数字递减 <bod ...
- [Atcoder Regular Contest 071 F & JZOJ5450]Neutral
题目大意 一个无限长的序列\(a\), 需要满足 1.数列中的每一个数在\(1\)到\(n\)之间. 2.对于\(i>=n, j>=n\), \(a_i=a_j\). 3.对于\(i< ...
- [学习笔记] Oracle体系结构、下载安装、创建实例、客户端工具、网络服务名、服务管理
Oracle体系结构 实例: 一个操作系统只有一个 Oracle 数据库 一个 Oracle 数据库可以有多个 Oracle 实例(通常只安装一个实例) 一个实例对应着一系列的后台进程和内存结构 表空 ...
- [ css ] 实现漂亮的输入框动画(借鉴自panjiachen的后台管理项目)
效果预览 HTML <div class="l-custom-input"> <input size="large" id="l-i ...
- Java 总结 数据底层原理 【包括 ArrayList、LinkedList、hash table、HashMap、Hashtable、ConcurrentHashMap、hash code、HashSet、LinkedHashMap、LinkedHashSet】
1.ArrayList (1)底层是由动态数组实现的[使用了List接口]. (2)动态数组是长度不固定,随着数据的增多而变长. (3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会 ...
- set类型转string[] 正确写法
测试源码: 1 @org.junit.Test 2 public void testSetType(){ 3 //测试set类型转string[] 4 // 5 Set<String> s ...