手动实现bind方法
Javascript天下第一 !
接上一篇文章,我们来一起实现一下bind方法
老规矩,来分析下bind的作用以及参数,返回值等信息
bind方法的全称是Function.prototype.bind(),官方是这么介绍的
bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项官方的描述直接介绍了bind做了什么
- 返回了一个新函数
- 将this(传入的参数)关键字绑定到该函数
- 参数合并,将bind函数的参数与原来的函数参数合并作为参数传给创建的新的函数
- 返回该函数
【手动实现bind方法】但是bind与call和apply又有区别,一个函数被call的时候,会直接去调用,但是bind是会返回一个函数,当这个函数执行的时候,bind()的第一个参数将作为它运行时的this。好了,bind就是做了这个事情,所以现在要一步步去实现它
- 首先去创建一个函数,这个函数需要有原函数的prototype的值
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
// 创建一个函数,
let fn = function(){}
//这个函数需要有所有的prototype的值
fn.prototype = this.prototype;
}
- 紧接着将this指向这个函数
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
//拿到当前方法
let _this = this;
//拿到指定的this值,shift操作会改变原数组
let target = Array.prototype.shift.call(_arguments);
// 创建一个函数,
let fn = function(){
_this.apply(target);
}
//这个函数需要有所有的prototype的值
fn.prototype = this.prototype;
}
- 然后合并参数(bind函数的参数与被bind函数的参数)
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
//拿到当前方法
let _this = this;
//拿到指定的this值,shift操作会改变原数组
let target = Array.prototype.shift.call(_arguments);
// 创建一个函数,
let fn = function(){
_this.apply(target,_arguments.concat(Array.prototype.slice.call(arguments)));
}
//这个函数需要有所有的prototype的值
fn.prototype = this.prototype;
}
- 最后将这个函数返回
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
//拿到当前方法
let _this = this;
//拿到指定的this值,shift操作会改变原数组
let target = Array.prototype.shift.call(_arguments);
// 创建一个函数,并执行合并之后的参数
let fn = function(){
_this.apply(target,_arguments.concat(Array.prototype.slice.call(arguments)));
}
//添加原函数所有的prototype的值
fn.prototype = this.prototype;
//最后返回这个方法
return fn
}
如此一个简单的bind方法就已经实现了。但是这里有个问题,就是当bind之后的函数,如果被当作构造函数去new的话,new出来的实例的指针指的是原来的bind方法,而不是bind之后的方法,这里有点绕,我先用变量来代表一下:
- A函数 ----代表原函数,也就是需要被bind的函数 A.bind(ctx);
- B函数 ----代表bind之后的函数,也就是B = A.bind(ctx);
- C函数 ----代表new一个B之后得到的实例函数 C = new B();
也就是说,如果用上面写的自定义bind方法,new出来的C的构造函数是A函数,但是我们需要的并不是A函数,我们要的是B函数
function A(name){
this.name = name
}
var obj = {}
var B = A._bind(obj)
var C = new B();
//按照上一章节的原型规则中的第四条
//所有的引用类型(数组,对象,函数),__ proto__属性指向它的构造函数的prototype属性值,这里C的构造函数是B
//但是这里却是false,
console.log(C.__proto__ === B.prototype)//false
//相反
console.log(C.__proto__ === A.prototype)//true
所以此时的C函数的构造函数是A并不是B,所以我们需要重新写一下上面的代码,需要判断返回的函数(B函数)是不是被当作构造函数使用的,怎么判断呢,其实很简单,判断B函数的this instanceof B是否是true,如果是的话,说明此时函数被当作构造函数来使用了,这个时候,apply里面的target不能使用外部传入的指针了,应该直接使用this,所以,整理一下,代码应该是这样:
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
//拿到当前方法
let _this = this;
//拿到指定的this值,shift操作会改变原数组
let target = Array.prototype.shift.call(_arguments);
// 创建一个函数,并执行合并之后的参数
let fn = function(){
let ctx = this instanceof fn ? this : target
_this.apply(ctx,_arguments.concat(Array.prototype.slice.call(arguments)));
}
//添加原函数所有的prototype的值
fn.prototype = this.prototype;
//最后返回这个方法
return fn
}
好了,这下是可以满足要求了,但是还是有一个小问题,由于对象属于引用类型,直接进行赋值语句操作的话,后面改动一个,另一个也会改变,因为指针指向的是同一个内存地址,所以上面代码中的最后一部分应该做一个clone
Function.prototype._bind = function(){
//首先去缓存参数列表,避免直接更改参数列表
let _arguments = arguments;
//类数组转换成数组
_arguments = Array.prototype.slice.call(_arguments);
//拿到当前方法
let _this = this;
//拿到指定的this值,shift操作会改变原数组
let target = Array.prototype.shift.call(_arguments);
// 创建一个函数,并执行合并之后的参数
let fn = function(){
let ctx = this instanceof fn ? this : target
_this.apply(ctx,_arguments.concat(Array.prototype.slice.call(arguments)));
}
//添加原函数所有的prototype的值
fn.prototype = Object.create(this.prototype);
//最后返回这个方法
return fn
}
好了,大功告成!
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树
- 人脸识别|【人脸识别系列】| 实现自动化妆
- paddle|动手从头实现LSTM
- pytorch|使用pytorch从头实现多层LSTM