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

bind的特性

var obj = {
a: 100,
say(one, two) {
console.log(this.a, one, two);
}
}
var obj2 = {
a: 300
}
var res = obj.say.bind(obj2, 1, 2);
res();
//300 1 2

可以看出:

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

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

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

function test(){
this.a = 10,
this.b = 20
};
var foo = {
a:200
}
var res = test.bind(foo);
var res2 = new res();
console.log(res2);
//test {a: 10, b: 20}

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

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

手把手教你实现bind

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

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

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

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

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

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

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

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

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

【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. 毕业设计 python opencv实现车牌识别 颜色定位

    主要代码参考https://blog.csdn.net/wzh191920/article/details/79589506 GitHub:https://github.com/yinghualuow ...

  2. 关于Django在写小项目的一些小注意事项

    个人常踩的坑的小问题: . 在筛选元素的时候,及时queryset里面只有一个元素,取值还是要用方法取出来 例:#当狗指定pd时候已经唯一,还是要加fir()方法,本人经常忘记了 models.Boo ...

  3. 内置函数_eval

    eval功能:将字符串str当成有效的表达式来求值并返回计算结果. 语法: eval(source[, globals[, locals]]) -> value 参数说明: expression ...

  4. linux及hadoop基本操作

    cd 命令:切换目录 () 切换到目录“/usr/local” ) 切换到当前目录的上一级目录   ) 切换到当前登录 Linux 系统的用户的自己的主文件夹    ls 命令:查看文件与目录   ...

  5. CheckStyle unable to read from stream

    “我在对比了其他正确的ChechStyle文件之后,发现这个无法导入的文件的编码和正确文件的编码不一样,我的xml文档编码为ANSI,而导入正确的ChechStyle文件为UTF-8编码,在我将自己的 ...

  6. Eclipse代码规范工具-Checkstyle安装和使用

    您首先可以参考这里:http://www.ibm.com/developerworks/cn/java/j-ap01117/index.html 那么首先您应该下载CheStyle: http://s ...

  7. SpringCloud+Redis

    redis①是一种nosql数据库,以键值对<key,value>的形式存储数据,其速度相比于MySQL之类的数据库,相当于内存读写与硬盘读写的差别,所以常常用作缓存,用于少写多读的场景下 ...

  8. python 几个常用自定义函数在dataframe上的应用

    最小值与最大值 def f(x): return pd.Series([x.min(),x.max(),index=['min','max']) frame.apply(f) 浮点值的格式化 form ...

  9. Flag-2019上半年

    1.坚持做博客记录,把日常学习到的技能以日记的形势进行呈现,每周进行总结: 2.2月底完成吴老师的机器学习视频课程: 3.6月底完成吴老师的深度学习视频课程: 相信自己,可以的,半年后见!

  10. (转)linux应用之test命令详细解析

    linux应用之test命令详细解析 原文:https://www.cnblogs.com/tankblog/p/6160808.html test命令用法. 功能:检查文件和比较值 1)判断表达式 ...