优雅手撕bind函数(面试官常问)
优雅手撕bind函数
前言:
- 为什么面试官总爱让实现一个bind函数?
- 他想从bind中知道些什么?
- 一个小小的bind里面内有玄机?
今天来刨析一下实现一个bind要懂多少相关知识点,也方便我们将零碎的知识点串联起来。
看完有用的同学记得点个赞再走,您的鼓励-我莫大的动力
看完能学到什么
- 实现bind
- new原理
本文章的叙事步骤
- bind函数作用
- 模拟bind的要点
- 实现思路
- new函数特殊情况(this&父原型)
-------------人工分割线-------------
bind函数的作用
返回一个能够改变this指向的函数。
模拟bind的要点
- 改变this指向
- 返回函数
实现思路
创建一个待返回的函数,函数内部利用call/apply改变指向,call/apply的参数从arguments中获取。
实现代码如下:
Function.prototype.myBind = function () {
let exeFunc = this;
let beThis = arguments[0];
let args = [].slice.call(arguments ,1);
return function () {
exeFunc.apply(beThis,args);
}
}
来份数据测试一下:
let other = {
name: 'other'
}
let obj = {
name: 'obj',
getName : function (age,height) {
console.log(this.name);
console.log('年龄' + age);
console.log('身高' + height);
}
}
obj.getName.myBind(other, 14, 200)();
测试结果正常。打印的是other
还挺简单的是吧!但考点通常不止如此。接着看:
function Person() {
this.name = 'person';
this.getName = function (age, height) {
console.log(this.name);
console.log('age:' + age, 'height:' + height);
}
}
这个时候:
let PersonMyBind = Person.myBind(window);
let per3 = new PersonMyBind();
per3.getName();
思考一下会打印person吗?
答案:实际上per3是一个空对象。
new函数特殊情况-this
那么为什么会出现这样的错误。这就牵扯到关于new的知识:
如果不太明白的可便宜看下这篇文章
这是一段关于new的模拟代码
function New (constructFunc) {
// 生命中间对象,最后作为返回的实例,相当于let obj = New(Obj); => obj = res
var res = {};
if(constructFunc.prototype !== null) {
// 将实例的原型指向构造函数的原型
res.__proto__ = constructFunc.prototype;
}
// 重点重点 ret为该构造函数执行的结果,将构造函数的this改为执行res
var ret = constructFunc.apply(res, Array.prototype.slice.call(arguments, 1));
// 如果构造函数有返回值,则直接返回
if((typeof rest === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
// 否则返回该实例
return res;
}
其中,下面一行代码就是导致我们写的bind不能如愿以偿将name、getName属性创建到对象的致命原因,且听我细细道来:
var ret = constructFunc.apply(res, Array.prototype.slice.call(arguments, 1));
当我们执行Person.myBind()的时候,我的得到的返回结果是一个函数:function () {exeFunc.apply(beThis,args);},来个图明显一点。
那么当这一行代码执行时:
var ret = constructFunc.apply(res, Array.prototype.slice.call(arguments, 1));
来张图来看清new Person与new PersonMyBind()的区别:
在知道产生这种现象的原因之后我们该如何解决?其实非常简单,如果是new的情况:
let resultFunc = function () {
exeFn.apply(this, args) // 这里传入的是this对象,对应着new过程中的res
}
所以这个时候问题就是该如何区分new Person()和Person()!答案还是在new的实现原理中找答案,我们可以找到上面new的模拟代码中的这一行:
// 将实例的原型指向构造函数的原型
res.__proto__ = constructFunc.prototype;
也就是说在执行
let resultFunc = function () {
// 此时的this__proto__等于Person.prototype
exeFn.apply(this, args)
}
此时的this.__proto__等于Person.prototype,利用这一特性就ok了。
升级我们的myBind
Function.prototype.myBind = function () {
if(typeof this !== 'function') {
throw new Error('调用者必须为function类型');
}
let exeFn = this; // this 为待执行函数
let currentThis = arguments[0]; // 待指定的this
let args = [].slice.call(arguments,1); // 剩余的都作为参数传递
let resultFunc = function () {
// 区分new调用与普通调用
exeFn.apply(this.__proto__=== resultFunc.prototype ? this : currentThis, args)
}
return resultFunc;
}
new函数特殊情况-父原型
到这里还没结束,我们还要解决Person加入有父原型的情况,在知道上面的知识点后解决这个也非常easy
再升级一版:
Function.prototype.myBind = function () {
if(typeof this !== 'function') {
throw new Error('调用者必须为function类型');
}
let exeFn = this; // this 为待执行函数
let currentThis = arguments[0]; // 待指定的this
let args = [].slice.call(arguments,1); // 剩余的都作为参数传递
let resultFunc = function () {
// 区分new调用跟普通调用
exeFn.apply(this.__proto__=== resultFunc.prototype ? this : currentThis, args)
}
// 维持原来函数的父原型
if (this.prototype) {
resultFunc.prototype = this.prototype;
}
return resultFunc;
}
打完收工
优雅手撕bind函数(面试官常问)的更多相关文章
- Android相关面试题---面试官常问问题
版权声明:本文为寻梦-finddreams原创文章,请关注: http://blog.csdn.net/finddreams/article/details/44513579 一般的面试流程是笔试完就 ...
- 面试官常问的Nginx的那几个问题?
什么是Nginx? Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器 Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代 ...
- 面试官常问的Nginx的几个问题
1.什么是Nginx? Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器 Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3 ...
- 面试官常问的20道Java题目(附答案)-来自Java1234
1. 以下代码的输出结果是(A) int i =3; i = i++; System.out.println(i); A .3 B.4 C.5 a=b++是先将b值赋值给a后b再自增. 2. Ma ...
- 面试官常问的10个Linux问题
1.如何暂停一个正在运行的进程,把其放在后台(不运行)? 为了停止正在运行的进程,让其再后台运行,我们可以使用组合键Ctrl+Z. 2.什么是安装Linux所需的最小分区数量,以及如何查看系统启动信息 ...
- JVM工作原理和特点(一些二逼的逼神面试官会问的问题)
作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...
- 【Nginx】面试官竟然问我Nginx如何生成缩略图,还好我看了这篇文章!!
写在前面 今天想写一篇使用Nginx如何生成缩略图的文章,想了半天题目也没想好,这个题目还是一名读者帮我起的.起因就是这位读者最近出去面试,面试官正好问了一个Nginx如何生成缩略图的问题.还别说,就 ...
- 面试官再问我如何保证 RocketMQ 不丢失消息,这回我笑了!
最近看了 @JavaGuide 发布的一篇『面试官问我如何保证Kafka不丢失消息?我哭了!』,这篇文章承接这个主题,来聊聊如何保证 RocketMQ 不丢失消息. 0x00. 消息的发送流程 一条消 ...
- 手写bind函数
实现bind函数 参考MDN提供的Polyfill方案 Function.prototype.myBind = function(context){ //这里对调用者做一个判断,如果不是函数类型,直接 ...
随机推荐
- Jquery的一键上传组件OCUpload及POI解析Excel文件
第一步:将js文件引入页面 <script type="text/javascript" src="${pageContext.request.contextPat ...
- Centos-内核核心组成
linux内核,相当于linux大脑,高可靠和高稳定都是针对内核来说 完整linux核心组成部分 1. 内存管理 合理有效的管理整个系统的物理内存,同时快速响应内核各子系统对内存分配的请求 2. 进程 ...
- mysql-8-subquery
#进阶8:子查询 /* 出现在其他语句中的select语句(嵌套) 分类: 按子查询出现的位置: SELECT 后面 FROM 后面 WHERE 或 HAVING 后面 EXISTS后面 */ USE ...
- QT博客:QT学习之路
http://www.qter.org/thread-629-1-1.html
- 在sqlserver中创建表
1:在sql语句中,临时表有两类,分别是局部(local)和全局(global)临时表,局部临时表只在其会话(事务)中可见,全局临时表可以被会话(事务)中的任何程序或者 模块访问 2:创建局部临时表 ...
- 018 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 12 数据类型转换的基本概念
018 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 12 数据类型转换的基本概念 本文知识点:Java中的数据类型转换 类型转换 类型转换分类 2类,分别是: ...
- Java知识系统回顾整理01基础01第一个程序03Eclipse下载安装
Eclipse是最流行的java 集成开发环境IDE(Integrated Development Environment) 下载安装Eclipse两种方式 一.方式1:Eclipse官网下载安装 链 ...
- sublime text3配置Python2、Python3的编译环境
由于Python2.Python3使用量都很高,Python3虽然是未来趋势,但是目前个别库还是只支持Python2.所以,很多人会选择在电脑上安装两个版本的Python,那么使用sublime执行代 ...
- 【漏洞复现】WinRAR目录穿越漏洞(CVE-2018-20250)复现
前言 这漏洞出来几天了,之前没怎么关注,但是这两天发现开始有利用这个漏洞进行挖矿和病毒传播了,于是想动手复现一波. WinRAR 代码执行相关的CVE 编号如下: CVE-2018-20250,CVE ...
- matlab中fft快速傅里叶变换
视频来源:https://www.bilibili.com/video/av51932171?t=628. 博文来源:https://ww2.mathworks.cn/help/matlab/ref/ ...