它们有什么用及区别?

在阐述它们如何使用之前,我们有必要整理清楚this的用法,简单的说thisJavaScript语言的一个关键字,它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。

那么问题又来了,this的值是什么呢?

因为this是在函数运行时,函数内部自动生成的一个对象,那么接下来我们通过函数来对this进行分析。 首先JavaScript中的函数可以分为两类:

  • 常规函数:函数声明式,函数表达式,构造函数
  • 箭头函数:(ES6引入使用)

接下来分别分析this在这些函数中究竟是什么?

理解常规函数中的this

1.纯粹的函数调用

function test(name) {console.log(name)console.log(this)
}
test('Jerry')  //调用函数
复制代码

以上函数调用的方式是非常常见的,然而这只是一种简写的形式,完整的写法应该如下:

function test(name) {console.log(name)console.log(this)
}
test.call(undefined, 'Tom')
复制代码

这里面便出现了我们将要学习的call,先不讨论它的作用,我们继续讨论this的用处,call方法接受的第一个参数就是this,但是我们这里是undefined,按照规定,如果你传的contextnull 或者 undefined,那么 window对象就是默认的context(严格模式下默认contextundefined)。

2.对象中函数的调用

const obj = {name: 'Jerry',greet: function() {console.log(this.name)}
}
obj.greet()  //第一种调用方法
obj.greet.call(obj) //第二种调用方法
复制代码

从上面的例子中,我们发现这次call方法的第一个参数为obj,此时说明函数greet内部的this指向了obj对象,这显而易见便知call方法的作用是改变this的指向,又因为上面两种调用方式结果一样可知函数的this指向可以理解为谁调用便指向谁。

3.构造函数中的this

每个构造函数在new之后都会返回一个对象,这个对象就是this,也就是context上下文。

理解箭头函数中的this

在使用箭头函数的时候,箭头函数会默认绑定外层的this值,所以在箭头函数中this的值和外层的this是一样的。因为箭头函数没有this,所以需要通过查找作用域链来确定this的值。

这就意味着如果箭头函数被非箭头函数包含, this绑定的就是最近一层非箭头函数的 this

注意:多层对象件套里面的this是和最外层保持一致的。

因为今天的重点是讲解callapplybind的用法及实现,然而箭头函数是没有这些方法的,所以箭头函数的使用仅限于此。

首先说明callapply是ES5中的语法,bind是ES6新引入的,它们三者的相似之处为:

  • 都是用来改变函数的this对象的指向
  • 第一个参数都是this要指向的对象
  • 都可以利用后续参数进行传参

不同之处使用一个例子进行说明:

const personOne = {name: "张三",age: 12,say: function () {console.log(this.name + ',' + this.age);}
}const personTwo = {name: "李四",age: 24
}personOne.say();    //张三,12
复制代码

对于以上的结果,我们应该都非常清楚,那么问题来了,如果我们想要知道personTwo对象的信息如何实现呢?

分别使用callapply以及bind方法实现,并从中得到它们三者的区别:

personOne.say.call(personTwo);       //李四,24
personOne.say.apply(personTwo);      //李四,24
personOne.say.bind(personTwo);       //没有输出任何东西
复制代码

修改以上代码对比可知:callapply都是对函数的直接调用,而bind方法返回的仍然是一个函数,因此我们需要执行它才会有结果。

personOne.say.call(personTwo);       //李四,24
personOne.say.apply(personTwo);      //李四,24
personOne.say.bind(personTwo)();       //李四,24
复制代码

接着继续讨论其余参数

const personOne = {name: "张三",age: 12,say: function (gender, phone) {console.log(this.name + ',' + this.age + ',' + gender + ',' + phone);}
}const personTwo = {name: "李四",age: 24
}personOne.say("女", "123");
复制代码

这个例子的区别于上面的即为say函数需要传递参数,我们分别使用这三种方法实现传递参数:

personOne.say.call(personTwo, "女", "123");       //李四,24,女,123
personOne.say.apply(personTwo, ["女", "123"]);    //李四,24,女,123
personOne.say.bind(personTwo, "女", "123")();     //李四,24,女,123
复制代码

显而易见的区别callbind除了第一个参数外,之后的参数均为一一传递,而apply除了第一个参数外,只有一个参数即为一个数组,数组中的每一项为函数需要的参数。

说明它们的用法以及区别之后,我们就要自己尝试着剖析它的原理,自己书写这三个方法啦~~~~~

call实现

在知道了它的使用即原理之后,想必直接看实现方法应该也可以理解的,那么先上代码:

Function.prototype.myCall = function (obj) {const object = obj || window; //如果第一个参数为空则默认指向window对象let args = [...arguments].slice(1); //存放参数的数组object.func = this;const result =  object.func (...args);delete object.func; //记住最后要删除掉临时添加的方法,否则obj就无缘无故多了个fnreturn result;
}
复制代码

代码非常简短,一步步进行说明解释:

因为call方法是每一个函数都拥有的,所以我们需要在Function.prototype上定义myCall,传递的参数obj即为call方法的第一个参数,说明this的指向,如果没有该参数,则指向默认为window对象。

args为一个存放除第一个参数以外的其余参数的数组(arguments为函数中接收到的多有参数,[...arguments]可以将arguments类数组转换为真正的数组,详细讲解可以查看ES6语法)。

解释object.func=this之前,我们先使用示例使用一下自己定义的myCall函数:

const personOne = {name: "张三",age: 12,say: function (gender, phone) {console.log(this.name + ',' + this.age + ',' + gender + ',' + phone);}
}const personTwo = {name: "李四",age: 24
}Function.prototype.myCall = function (obj) {const object = obj || window; //如果第一个参数为空则默认指向window对象let args = [...arguments].slice(1); //存放参数的数组object.func = this;const result =  object.func (...args);delete object.func; //记住最后要删除掉临时添加的方法,否则object就无缘无故多了个funcreturn result;
}personOne.say.myCall(personTwo,"女",18333669807);   //李四,24,女,18333669807
复制代码

根据示例,我们进行解释,myCall里面的this指的是personOne.say这个方法(因为myCall是一个方法,上面所说的,谁调用它,它的this便指向谁),object.func=this相当于给object这个对象克隆了一个personOne.say方法,让object在调用这个方法,相当于object.personOne.say,达到了call的效果。(记住最后要删除掉临时添加的方法,否则object就无缘无故多了个func

object.func (...args)里面的参数即为传入的其余参数。

apply实现

通过上面的分析,想必大家应该已经基本明白了它是如何实现的了,那么接下来实现apply就非常简单了,因为两者的区别主要就是参数的传递方式不同,和上面一样,先直接看一下代码:

Function.prototype.myApply = function (obj) {const object = obj || window; //如果第一个参数为空则默认指向window对象if (arguments.length > 1) {var args = arguments[1]; //存放参数的数组} else {var args = []; //存放参数的数组}object.func = this;const result = object.func(...args);delete object.func; //记住最后要删除掉临时添加的方法,否则obj就无缘无故多了个fnreturn result;
}personOne.say.myApply(personTwo, ["女", 24]);
复制代码

主要区别就是获取参数不同,因为apply的带二个参数为数组,数组中包含函数需要的各项参数值,其余内容实现myCall相同,此处就不在做解释。

bind实现

话不多说,依旧是先上代码

Function.prototype.myBind = function (obj) {const object = obj || window; //如果第一个参数为空则默认指向window对象let self = this;let args = [...arguments].slice(1); //存放参数的数组return function () {let newArgs = [...arguments]return self.apply(object, args.concat(newArgs))}
}personOne.say.myBind(personTwo, "女", 24)();
复制代码

前面的知识不重复说,return function是因为bind返回的是一个函数,并且这个函数不会执行,需要我们再次调用,那么当我们调用的时候,我们依旧可以对这个函数进行传递参数,即为支持柯里化形式传参,所以需要在返回的函数中声明一个空的数组接收调用bind函数返回的函数时传递的参数,之后对两次的参数使用concat()方法进行连接,调用ES5中的apply方法。

到此为止,这三种方法的使用,区别以及实现已经都讲述完了,希望这篇文章对大家有所帮助~~

手写call,apply和bind(分析三者的用法与区别)相关推荐

  1. 手写一个原神祈愿分析工具

    手写一个原神祈愿分析工具 之前一直通过游创工坊来进行祈愿抽卡数据分析,但是广告太多,而且担心auth_key泄露,于是自己花了一天时间动手实现了个数据分析工具,数据永久保存在本地,没有信息泄露风险,话 ...

  2. JavaScript中的call,apply,bind区别及应用(包含手写call/apply/bind)

    目录 一.使用目的 二.三者分别是如何定义的及区别(摘自MDN) 三.在程序中收获 四.三者的具体应用 四.手写bind,apply,call 今天在读程序题的时候,遇到call,apply,bind ...

  3. 手写bind_深入理解 JavaScript 之手写 call, apply, bind 方法

    这是老生常谈的手写了,今天想自己试着实现一下,做个笔记. call 方法 Function.prototype.myCall = function (context) { if (context == ...

  4. matlab 对mnist手写数字数据集进行判决分析_人工智能TensorFlow(十四)MINIST手写数字识别...

    MNIST是一个简单的视觉计算数据集,它是像下面这样手写的数字图片: MNIST 每张图片还额外有一个标签记录了图片上数字是几,例如上面几张图的标签就是:5.0.4.1. MINIST数据 MINIS ...

  5. matlab 对mnist手写数字数据集进行判决分析_Python神经网络编程:手写数字的数据集MNIST...

    识别人的笔迹这个问题相对复杂,也非常模糊,因此这是一种检验人工智能的理想挑战.这不像进行大量数字相乘那样明确清晰. 让计算机准确区分图像中包含的内容,有时也称之为图像识别问题.科学家对这个问题进行了几 ...

  6. 手写call,apply,bind函数

    涉及面试题 call,apply,bind函数内部实现是怎样的? 考虑两点: 第一个参数为undefined或null的时候,那么会转变为window 改变了this执行,让新的对象可以执行该函数. ...

  7. java return true false_javascript中return,return true,return false三者的用法及区别

    1.语法及返回方式 ①返回控制与函数结果 语法为:return 表达式; 语句结果函数的执行,返回调用函数,而且把表达式的值作为函数结果返回出去 ②返回控制无函数结果 语法为:return; 在大多数 ...

  8. 手写一切(updating...)

    1.手写ajax基本过程 var xhr; if(window.XMLHttpRequest){//code for IE7+,Firefox,Chrome,Opera,Safarixhr = new ...

  9. android app用百度ocr识别sdk实现手写扫描功能(一)

    目录 1.概述 2.准备工作 3.sdk源码分析 3.1licence方式校验方式 3.2手写文字识别功能的分析

最新文章

  1. iptables配置-Linux系统安全防火墙
  2. python适合做后端开发吗-Python真的不适合做后端开发语言吗?
  3. (第六场)Singing Contest 【模拟】
  4. mysql 修改表id值_修改数据库中表的id
  5. Windows上mount NFS V4
  6. React开发(166):ant design form 设置值
  7. 学习:SQL Server的BUILTIN\Administrators用户
  8. ViewData 和TempData ,Session用法
  9. 485. 最大连续1的个数
  10. python django步骤_python - django (ORM使用步骤)
  11. 57 spi电平转换的坑
  12. aspupload ,在winows server 2008 下无法使用
  13. OSPF学习笔记整理
  14. Multi-Object Trackers
  15. 【matplotlib】plot()kind参数表
  16. 软文写作技巧与营销的相互作用
  17. 栈evaluate-reverse-polish-notation-leetcode练习题
  18. 新品发布季第二场,APT威胁挖掘机「NDR流量监测系统」正式亮相
  19. cad墙线打断lisp_cad相切命令(cad相交线自动打断)
  20. 2018 mysql 笔试题_2018秋招数据库笔试面试题汇总

热门文章

  1. 史上最全的Linux常用命令汇总①收藏这一篇就够了!(超全,超详细)
  2. python笔记之if练习
  3. 数据结构与算法常见笔试题 .
  4. r生成新的dataframe_2020-08-11R语言中dataframe与list的转换方法
  5. ospf hello时间和dead_图文并茂解释OSPF邻居关系建立失败的几种常见情况(太实用了!)...
  6. nginx 正则 结尾 配置_nginx location 配置阐述优先级别使用说明-不当可能存在安全隐患...
  7. java中io流如何创建一个文件_,Java中Io流操作-File类的常用操作-创建文件,创建文件夹...
  8. 历史快照_实用脚本--合理估算oracle数据库及数据库对象历史增长情况
  9. 搭建kafaka_Kafka 环境部署搭建
  10. 安川g7接线端子图_ABB、KUKA、FANUC、安川四大家族机器人安全回路小结