【js基础修炼之路】- 手把手教你实现bind
手写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的更多相关文章
- 【js基础修炼之路】- 微任务,宏任务和Event-Loop
一段代码让你了解Event-Loop console.log(1); setTimeout(() => { console.log(2); }, 0); new Promise((resolve ...
- 【js基础修炼之路】— 深入浅出理解闭包
之前对于闭包的理解只是很肤浅的,只是浮于表面,这次深究了一下闭包,下面是我对闭包的理解. 什么是闭包? 引用高程里的话 => 闭包就是有权访问另一个作用域中变量的函数,闭包是由函数以及创建该函数 ...
- 【js基础修炼之路】— 我理解的原型链
提起原型链,大家并不陌生,但是对于新人来说一提到原型方面的东西就会比较懵.在我自一次面试的时候,面试官也给我提了这样的问题,当时就按照我的理解说了一些,但是很肤浅,在此我希望给刚入门的前端小伙伴聊一下 ...
- 【js基础修炼之路】— null和undefined的区别
在近期的复习期间遇到null和nudefined,于是通过查找资料,想写一篇文章来说明他们的区别.. javaScript高级程序设计: 在使用var声明变量但未对其加以初始化时,这个变量的值就是un ...
- 【js基础修炼之路】--创建文档碎片document.createDocumentFragment()
讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么. 在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面就会更新并反映出这个变化,对于 ...
- 【css基础修炼之路】— 谈谈元素的垂直水平居中
作为一个初级的前端工程师,在开发的过程中遇到了许多问题,其中使元素垂直居中这个问题难住了我,可能在大家看来这是一个非常小的问题,但是却困扰了我很长时间,于是决定做一个总结!!! 废话不多说,直接上代码 ...
- 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发
每天记录一点:NetCore获得配置文件 appsettings.json 用NetCore做项目如果用EF ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...
- 手把手教你用JS/Vue/React实现幸运水果机(80后情怀之作)
项目体验地址 免费视频教程 分别使用原生JS,Vue和React,手把手教你开发一个H5小游戏,快速上手Vue和React框架的使用. 项目截图 在线体验 在线体验 游戏介绍 幸运水果机是一款街机游戏 ...
- Python之手把手教你用JS逆向爬取网易云40万+评论并用stylecloud炫酷词云进行情感分析
本文借鉴了@平胸小仙女的知乎回复 https://www.zhihu.com/question/36081767 写在前面: 文章有点长,操作有点复杂,需要代码的直接去文末即可.想要学习的需要有点耐心 ...
随机推荐
- 1001 害死人不偿命的(3n+1)猜想 (15)(15 分)
卡拉兹(Callatz)猜想: 对任何一个自然数n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把(3n+1)砍掉一半.这样一直反复砍下去,最后一定在某一步得到n=1.卡拉兹在1950年的世界数 ...
- 关于c语言的位运算&,|,^(看懂汉字的都能看懂)
其中|,&可以当作逻辑运算符,当|,&当成逻辑运算符时,与||,&&的用法基本相似,&&,||运算时会当前面的表达式能够决定整个表达式,则不进行对后面的 ...
- vue2.0组件的生命周期
beforeCreate(){ console.log(new Date().getTime()) let data = this.text; console.log('组件创立之前') consol ...
- nginx -s reload "/alidata/server/nginx/logs/nginx.pid" failed
[root@snoopy :: vhosts]# nginx -s reload nginx: [error] open() : No such file or directory) 修改完nginx ...
- Java日志组件1---Jdk自带Logger(java.util.logging.Logger)
最近在看日志的一些东西,发现利用JDK自带的log也可以简单的实现日志的输出,将日志写入文件的过程记录如下: 1.新建LogUtil.Java( 里面写了几个静态方法,为log设置等级.添加log控制 ...
- Spring集成Quartz的3种方式
1.使用xml配置方式 Maven依赖 <properties> <!-- spring版本号 --> <spring.version>4.2.2.RELEASE& ...
- leetcode 175 Combine Two Tables join用法
https://leetcode.com/problemset/database/ ACM退役,刚好要学sql,一定要刷题才行,leetcode吧. 这一题,说了两个表,一个左一个右吧,左边的pers ...
- Sqlite操作的一些关键类的官方说明与Intent的startactivityforresult方法
Intent: 该功能可以用于通过intent来跳转界面时候传递信号给原理的页面,以便做出一些处理: sqlite的使用: 该方法得到的sqlitedatabase可读可写,而getreadabled ...
- 案例51-crm练习新增客户使用数据字典和ajax
1 案例效果 2 使用ajax加载数据字典下拉选-后台部分 1 domain部分-BaseDict package www.test.domain; public class BaseDict { / ...
- empty,isset,is_null比较(差异与异同)
做php开发时候,想必在使用:empty,isset,is_null 这几个函数时候,遇到一些问题.甚至给自己的程序带来一些安全隐患的bug.很多时候,对于isset,empty都认为差不多.因此开 ...