在阅读本文之前,您应该先首先对call或者apply、new操作符的原理、柯里化以及原型链机制有一定了解。
1.bind方法的原理
fn(p1,p2,p3)
调用bind方法时,fn.bind(obj)
,会返回一个this指向obj的函数bindFn
,bindFn
与fn
的函数体
相同,而且bind可以只接收原函数的一部分参数,让它返回的函数再去处理其他的参数
,即函数柯里化。
2.bind的第一次简单实现
因为bind返回值是一个函数,所以我们就需要考虑函数调用的方式了,如果该函数是作为构造函数,通过new操作符调用的话和直接调用结果应该是相同的吗?在实现bind的过程中需不需要判断函数的调用方式呢?我对此感到疑惑,然后我就首先实现了一个不判断调用方式的简单版本:
//不考虑构造函数
Function.prototype.myBind = function (thisValue, ...firstArgs) {
//thisValue是返回值函数要绑定的this firstArgs是第一次传入的参数数组
const currentFn = this;
//在执行myBind之后 得到的是一个函数体和调用myBind的函数体相同的函数 只是返回值函数中 绑定了this
return function (...secondArgs) {
//secondArgs是第二次传入的参数数组
return currentFn.apply(thisValue, [...firstArgs, ...secondArgs])
}
}
function add(a, b, c) {
console.log(this);
return a + b + c
}
console.log(add(1, 2, 3));
console.log(add.bind(null, 1)(2, 3));
console.log(add.bind(obj, 1)(2, 3));
console.log(add.myBind(null, 1)(2, 3));
console.log(add.myBind(obj, 1)(2, 3));
但是如果是构造函数的话,结果就会有所不同
function Person(n, a, s) {
console.log(this);
this.name = n
this.age = a
this.sex = s
}
Person('小明', 18, '男')//this 指向window
const p = new Person('小花', 19, '男')//this 指向p
const bindPerson = Person.bind(obj)
const myBindPerson = Person.myBind(obj)
const p2 = new bindPerson('xiaogou', 17, 'male')//指向p2
const p3 = new myBindPerson('xiaogou', 17, 'male')//指向obj
这是因为虽然new myBindPerson()
时,new
操作符将myBindPerson
内部的this
指向了当前实例p3
,但是myBindPerson在执行到return语句currentFn.apply(thisValue,[])
一句时,又将this强制绑定成为了obj
,所以最终输出的this是obj,显然,在此时我们应当判断是否是由new操作符调用的函数。
根据new操作符的原理,我们可以采用instanceof的方法来判断。
3.bind实现
Function.prototype.myBind = function (thisValue, ...firstArgs) {
//thisValue是返回值函数要绑定的this firstArgs是第一次传入的参数数组
const currentFn = this;
//创建一个空的中转函数 用于检测是否使用了new
const tempFn = function () { }
//如果currentFn是一个构造函数 他的prototye存在 将中转函数的prototype指向当前函数的prototype 维护原型链关系
if (currentFn.prototype) {
tempFn.prototype = currentFn.prototype
}
//此时 将tempFn创建的实例 作为返回值函数BindFn的prototype
//以后通过new bindFn() 创建的实例的__proto__就指向了 new Temp()
//new TempFn() 就被添加到了该原型链之上 就可以通过instanceof 判断对bindFn的调用有没有使用new
bindFn.prototype = new tempFn();//将来能够使用instanceof判断的关键语句
function bindFn(...secondArgs) {
//如果调用时使用了new 即 const p=new bindFn()形式 此时this指向p 又因为tempFn的实例是p的原型对象
//所以 p instanceof tempFn 应当为true 此时我们不应改变this指向
//如果是直接调用bindFn() this应该指向window 这时候我们需要绑定this
return currentFn.apply(this instanceof tempFn ? this : thisValue, [...firstArgs, ...secondArgs])
}
return bindFn
}
Person('小明', 18, '男')//this 指向window
const p = new Person('小花', 19, '男')//this 指向p
const bindPerson = Person.bind(obj)
const myBindPerson = Person.myBind(obj)
const p2 = new bindPerson('xiaogou', 17, 'male')//指向p2
const p3 = new myBindPerson('xiaogou', 17, 'male')//指向p3 bindFn的实例
4.bind的mdn实现
我参考了mdn,mdn实现是:
if(!Function.prototype.bind){
Function.prototype.bind = function(oThis){
if(typeof this !== 'function'){
throw new TypeError('被绑定的对象需要是函数')
}
var self = this
var args = [].slice.call(arguments, 1)
var func = function(){}
fBound = function(){
return self.apply(this instanceof func ? this : oThis, args.concat([].slice.call(arguments)))
}
if(this.prototype){
func.prototype = this.prototype
}
fBound.prototype = new func()
return fBound
}
}
我利用声明提升机制,将
var func = function(){}
if(this.prototype){
func.prototype = this.prototype
}
fBound.prototype = new func()
放在了一起,为的是便于理解中转函数func的作用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。