优雅手撕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){ //这里对调用者做一个判断,如果不是函数类型,直接 ...
随机推荐
- zookeeper watch笔记
ZK其核心原理满足CP, 实现的是最终一致性, 它只保证顺序一致性. zookeeper 基于 zxid 以及阻塞队列的方式来实现请求的顺序一致性.如果一个client连接到一个最新的 followe ...
- nessus 家庭版安装教程(windows)
1.根据系统选择安装包:https://www.tenable.com/products/nessus/select-your-operating-system 2.申请激活码,在我们安装的时候差不多 ...
- >>8) & 0xFF中的 >> 和 &0xFF 的作用
参考:https://blog.csdn.net/iamgamer/article/details/79354617 其中有两个位运算,一个是>>,一个是&. 0xff的作用一: ...
- Jmeter5.3源码编译
下载源码 https://jmeter.apache.org/download_jmeter.cgi 配置网络环境(重要) 下载 Proxifier 配置上网条件 导入Idea 通过 Idea 的 O ...
- Nginx 配置 http 强制跳转到 https
个人真实配置 架构:Nginx 反向代理 + Nginx 前端(LNMP) 在 Nginx 反向代理的 虚拟机主机配置文件中,作如下配置: upstream ilexa_cn { server 192 ...
- 2018年10月份编程语言排行榜(来自TIOBE Index for October 2018)
TIOBE Index for October 2018 from:https://www.tiobe.com/tiobe-index// October Headline: Swift is kno ...
- DM9000裸机驱动程序设计
对于任何一个硬件模块的设计,首先第一步都是要先了解硬件本身后,再开始程序的软件设计.而由于DM9000的芯片文档内容很多,要驱动好网卡,需要很长时间,特别对于新手比较困难,所以可以参考linux内核代 ...
- linq 整理(前序)
前言 对linq进行整理,分为前序.中序和后序. 前序就是一些简单的概念和模拟. 中序的话就是深挖一些思想. 后序对其进行解刨. 正文 语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# ...
- IDEA SpringBoot+JPA+MySql+Redis+RabbitMQ 秒杀系统
先放上github地址:spike-system,可以直接下载完整项目运行测试 SpringBoot+JPA+MySql+Redis+RabbitMQ 秒杀系统 技术栈:SpringBoot, MyS ...
- 学习ing
1.从硬件和逻辑两个角度探讨什么是内存?硬件上看,内存就是电脑上的硬件--内存条.内存通过内存条不同的实现原谅分为DRAM(DRAM已经发展出好多代)和SRAM.从逻辑的角度来说,内存就是一个可以随机 ...