手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind。

bind的特性

  1. var obj = {
  2. a: 100,
  3. say(one, two) {
  4. console.log(this.a, one, two);
  5. }
  6. }
  7. var obj2 = {
  8. a: 300
  9. }
  10. var res = obj.say.bind(obj2, 1, 2);
  11. res();
  12. //300 1 2

可以看出:

  • bind是函数的方法,只有函数可以调用

  • bind的第一个参数是this指向,剩下的参数作为调用者的参数

  • bind方法返回的是一个函数,需要再次调用才能执行

  1. function test(){
  2. this.a = 10,
  3. this.b = 20
  4. };
  5. var foo = {
  6. a:200
  7. }
  8. var res = test.bind(foo);
  9. var res2 = new res();
  10. console.log(res2);
  11. //test {a: 10, b: 20}

从上面可以看出,new之后this执行不跟随bind的第一个参数了,知道new是怎么实现的小伙伴一定知道为什么(不知道的可以看一下这篇文章的末尾 https://juejin.im/post/5c492603e51d451d200e4ebb ) 此时的this指向了res2。

知道了bind的特性,下面我们来实现一下bind

手把手教你实现bind

我们知道bind返回的是一个函数,调用者也是一个函数,并且bind改变了this指向,而且bind还可以传参,下面我们来实现一下这些功能:

  1. Function.prototype.bind = function(oThis) {
  2. // 判断调用者是不是函数
  3. if(typeof this != 'function'){
  4. throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  5. }
  6. //保存this(this指向调用bind者)
  7. var fToBind = this;
  8. //获取传入bind函数的第二个及其后面的参数(除去this参数)
  9. var aArgs = Array.prototype.slice.call(arguments,1);
  10. var fBound = function(){
  11. //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
  12. //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
  13. return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
  14. }
  15. // 返回新函数
  16. return fBound;
  17. }

上面的代码实现了一个基本的bind,但是还有一些问题,例如上面只是绑定了this,但是原函数的原型新函数并没有继承,所以我们需要再次继承一下原型:

  1. Function.prototype.bind = function(oThis) {
  2. // 判断调用者是不是函数
  3. if(typeof this != 'function'){
  4. throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  5. }
  6. //保存this(this指向调用bind者)
  7. var fToBind = this;
  8. //获取传入bind函数的第二个及其后面的参数(除去this参数)
  9. var aArgs = Array.prototype.slice.call(arguments,1);
  10. var fBound = function(){
  11. //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
  12. //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
  13. return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
  14. }
  15. //绑定原型
  16. fBound.prototype = this.prototype;
  17. // 返回新函数
  18. return fBound;
  19. }

本以为大功告成,但是还有一个问题,看下面的例子:

  1. function bar() {}
  2. var bindFoo = bar.bind(null);
  3. bindFoo.prototype.value = 1;
  4. console.log(bar.prototype.value) // 1

我只改变了bindFoo的原型,bar的为什么也跟着变了,因为在写bind的时候把bar的原型赋给了bindFoo,所以导致了这种情况,下面我们用一个中转的函数来解决这个问题:

  1. Function.prototype.bind = function(oThis) {
  2. // 判断调用者是不是函数
  3. if(typeof this != 'function'){
  4. throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  5. }
  6. //保存this(this指向调用bind者)
  7. var fToBind = this;
  8. //获取传入bind函数的第二个及其后面的参数(除去this参数)
  9. var aArgs = Array.prototype.slice.call(arguments,1);
  10. var fNOP = function() {};
  11. var fBound = function(){
  12. //this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
  13. //== false的时候说明当做了普通函数来调用,this为bind的第一个参数
  14. return fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
  15. }
  16. // 为了让 fBound 构造的实例能够继承绑定函数的原型中的值
  17. if (this.prototype) {
  18. fNOP.prototype = this.prototype;
  19. }
  20. // 下行的代码使fBound.prototype是fNOP的实例,因此
  21. // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
  22. fBound.prototype = new fNOP();
  23. // 返回新函数
  24. return fBound;
  25. }

对于代码有疑问的小伙伴可以留言或者看注释!!!

【js基础修炼之路】- 手把手教你实现bind的更多相关文章

  1. 【js基础修炼之路】- 微任务,宏任务和Event-Loop

    一段代码让你了解Event-Loop console.log(1); setTimeout(() => { console.log(2); }, 0); new Promise((resolve ...

  2. 【js基础修炼之路】— 深入浅出理解闭包

    之前对于闭包的理解只是很肤浅的,只是浮于表面,这次深究了一下闭包,下面是我对闭包的理解. 什么是闭包? 引用高程里的话 => 闭包就是有权访问另一个作用域中变量的函数,闭包是由函数以及创建该函数 ...

  3. 【js基础修炼之路】— 我理解的原型链

    提起原型链,大家并不陌生,但是对于新人来说一提到原型方面的东西就会比较懵.在我自一次面试的时候,面试官也给我提了这样的问题,当时就按照我的理解说了一些,但是很肤浅,在此我希望给刚入门的前端小伙伴聊一下 ...

  4. 【js基础修炼之路】— null和undefined的区别

    在近期的复习期间遇到null和nudefined,于是通过查找资料,想写一篇文章来说明他们的区别.. javaScript高级程序设计: 在使用var声明变量但未对其加以初始化时,这个变量的值就是un ...

  5. 【js基础修炼之路】--创建文档碎片document.createDocumentFragment()

          讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么.         在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面就会更新并反映出这个变化,对于 ...

  6. 【css基础修炼之路】— 谈谈元素的垂直水平居中

    作为一个初级的前端工程师,在开发的过程中遇到了许多问题,其中使元素垂直居中这个问题难住了我,可能在大家看来这是一个非常小的问题,但是却困扰了我很长时间,于是决定做一个总结!!! 废话不多说,直接上代码 ...

  7. 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发

    每天记录一点:NetCore获得配置文件 appsettings.json   用NetCore做项目如果用EF  ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...

  8. 手把手教你用JS/Vue/React实现幸运水果机(80后情怀之作)

    项目体验地址 免费视频教程 分别使用原生JS,Vue和React,手把手教你开发一个H5小游戏,快速上手Vue和React框架的使用. 项目截图 在线体验 在线体验 游戏介绍 幸运水果机是一款街机游戏 ...

  9. Python之手把手教你用JS逆向爬取网易云40万+评论并用stylecloud炫酷词云进行情感分析

    本文借鉴了@平胸小仙女的知乎回复 https://www.zhihu.com/question/36081767 写在前面: 文章有点长,操作有点复杂,需要代码的直接去文末即可.想要学习的需要有点耐心 ...

随机推荐

  1. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_友元程序集

    [应用场景] 程序集A访问程序集B定义的Internal访问类型的类的成员. [使用方式] 在构建程序集B的时候,引入System.Runtime.CompilerServices,以此来添加Inte ...

  2. Gym - 101572D Distinctive Character bfs 思维

    题目传送门 题目大意: 给出n个01串,让你构造一个字符串,使这个字符串和这些字符串中相似程度最高 尽可能低.如果两个字符串对应位置相同,则相似程度加一. 思路: 每一个01串更改自己的一部分后,都可 ...

  3. Educational Codeforces Round 13 A

    Description Johny likes numbers n and k very much. Now Johny wants to find the smallest integer x gr ...

  4. java——删除链表中等于给定值的所有元素

    class ListNode{ int val ; ListNode next; public ListNode(int x) { val = x; } public ListNode(int[] a ...

  5. c++ for_each( )学习

    for_each()事实上是個 function template,其实质如下  [effective STL item 41] template<typename InputIterator, ...

  6. Ancient Messages UVA - 1103

    题目链接:https://vjudge.net/problem/UVA-1103 题目大意:每组数据包含H行W列的字符矩阵(H<=200,W<=50) 每个字符为为16进制  你需要把它转 ...

  7. [转]如何在.NET MVC中使用jQuery并返回JSON数据

    本文转自:http://blog.sina.com.cn/s/blog_48e42dc90100xp1p.html 二.开始实践 - jQuery端 假设我们要从服务器端获取一个文章列表,并把文章条目 ...

  8. Andrew Ng 的 Machine Learning 课程学习 (week5) Neural Network Learning

    这学期一直在跟进 Coursera上的 Machina Learning 公开课, 老师Andrew Ng是coursera的创始人之一,Machine Learning方面的大牛.这门课程对想要了解 ...

  9. AJAX重点知识的心得体会

    下面就为大家带来一篇 AJAX重点知识的心得体会.学习还是有点帮助的,给大家做个参考吧. AJAX是什么? 是Asynchronous Javascript And XML的首字母的缩写, 它不是一门 ...

  10. Golang笔记(二)面向对象的设计

    Golang笔记(二)面向对象的设计 Golang本质还是面向过程的语言,但它实现了一些OOP的特性,包括抽象.封装.继承和多态. 抽象和封装 Golang和C语言一样以struct为数据结构核心,不 ...