想要学好一样东西,就要去探究它的本质,懂的举一反三,而不仅仅是跟着视频写几段代码就能学会的,还是要不断的去钻研学习。下面我们来说一下bind函数,首先看一下官方是怎么定义的;
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
通过这个官方的介绍我们知道的事bind 返回一个新函数,第二个参数改变的目标函数的this指向,后面都是传入的参数。
function a(){ console.log(this); console.log(arguments); } var func=a.bind([1,2,3],1,2,3); func(4,5,6); //(3) [1, 2, 3] Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]
通过这个例子可以发现返回的新函数this指向传入的第一个参数[1,2,3] ,函数内部的arguments 代表传入的参数列表。现在我们先来实现一个简单的bind函数。
Function.prototype.customeBind=function(context,...arg){ var _this=this; return function(...list){ _this.call(context,...arg,...list); } } var func1=a.customeBind([1,2,3],1,2,3); func1(4,5,6); //(3) [1, 2, 3] Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]
这时候我们会发现得到的结果是一样的,需要主要的事bind函数不仅仅目标函数可以传入参数进来,返回的新函数也是可以传入参数,通过上面的列子就可以看得很清楚,但bind函数就这么结束了吗?很明显不是,因为在我们js里面还有一种很特别的函数叫做构造函数,如果我们返回的函数作为构造函数,那么结果是怎么样呢?
function a(){ console.log(this); } a.prototype.age="12"; var func=a.bind([1,2,3],1,2,3); var c=new func(); //a {} console.log(c.age) //12
可以看到当我们返回的函数作为构造函数时,我们传入的第一个参数就失效了,this就会指向新创建出来的实例的,但是参数列表依然生效,并且目标函数原型上的方法新实例也完全继承了下来,下面我们来进一步优化我们的代码:
Function.prototype.customeBind = function (context, ...arg) { var _this = this; var func = function (...list) { _this.call(this instanceof func ? this : context, ...arg, ...list); } func.prototype = _this.prototype; return func; } var func1 = a.customeBind([1, 2, 3], 1, 2, 3); var c = new func1(); //func {} console.log(c.age) //12
可以看到bind函数的基本功能已经实现了,但是我们回过头再看看我们的新函数是如何继承他的目标函数的属性的,我们把新函数的原型对象指向了目标函数的原型对象,我们都知道在js里面对象是一个引用类型,他保存的是一个地址,只要改变两个对象都会跟着一起改变,如果我们在返回的新函数上面原型添加属性,那么目标函数也会跟着改变,很明显这样是不严谨的,这个时候我们需要一个中转函数来替我们工作,下面附上最后的代码:
Function.prototype.customeBind = function (context, ...arg) { var _this = this; var fn=new Function(); var func= function (...list) { _this.call(this instanceof func ? this : context, ...arg, ...list); } fn.prototype=_this.prototype; func.prototype =new fn(); return func; }