bind官方说明bind()函数创建一个新函数(称为绑定函数),该函数与被调用函数(绑定函数的目标函数)具有相同的函数体(ECMAScript5规范中的内置调用属性)。this值在调用目标函数时绑定到bind()的第一个参数,不能被覆盖。调用绑定函数时,bind()也接受预设参数提供给原函数。绑定函数也可以使用new运算符创建对象:这与作为构造函数的原始函数一样。提供的this值将被忽略,并将调用时参数提供给模拟函数。用法介绍由于javascript中的作用域是由其运行的环境决定的,所以函数定义往往与实际运行的环境不同,因此作用域也会随之变化。比如下面这种情况:varid='window';//定义一个函数,但是不立即执行vartest=function(){console.log(this.id)}test()//window//通过测试作为参数varobj={id:'obj',hehe:test}//此时test函数的运行环境发生了变化obj.hehe()//'obj'//为了避免这种情况,javascript中有一个bind方法,可以在函数运行前绑定其作用域,修改如下varid='window';vartest=function(){console.log(this.id)}.bind(window)varobj={id:'obj',hehe:test}test()//windowobj.hehe()//windowbind方法的一个重要作用是将一个函数绑定到一个scope上,但是bind方法不兼容lower版本浏览器,这里我们可以手动实现。分解关键思路,因为bind方法不会立即执行函数,需要返回一个要执行的函数(这里使用闭包,可以返回一个函数)returnfunction(){}scopebinding,applyorcall这里可以使用方法来实现xx.call(yy)/xx.apply(yy)传参,由于参数的不确定性,需要使用apply传数组(例子比较清楚)xx.apply(yy,[...Array...]),用call不方便,因为call后的参数需要一一罗列才能实现上面的思路,粗略的原型已经很清楚了,代码也应该很容易实现绑定作用域,绑定传递参考Function.prototype.testBind=function(that){var_this=this,/**因为参数的不确定性,所以和arguments统一处理。这里的参数只是一个具有长度属性的类数组对象*数组的一个切片可以用方法转换成一个标准格式的数组。除了scope对象,*之后的所有参数都需要作为数组参数传递*Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1)*/slice=Array.prototype.slice,args=slice.apply(arguments,[1]);//返回函数returnfunction(){//apply绑定作用域,传参return_this.apply(that,args)}}testvartest=function(a,b){console.log('scopebinding'+this.value)console.log('testBindparameterpassing'+a.value2)console.log('callParameterpassing'+b)}varobj={value:'ok'}varfun_new=test.testBind(obj,{value2:'alsook'})fun_new('hellobind')//作用域绑定ok//testBind参数传递Alsoook//调用参数传递未定义的动态参数作用域绑定上面已经实现了bind方法,美中不足的是由于我们返回的是一个函数,调用它应该支持传递参数。显然,上面的fun_new调用是不支持传参的。只能在testBind绑定的时候传参,因为我们最后调用的返回函数是function(){return_this.apply(that,args)}绑定的时候this里的args已经确定了,调用的时候已经固定了value,我们没有处理过这个函数传递的参数我们将其转化为returnfunction(){return_this.apply(that,args.concat(Array.prototype.slice.apply(arguments,[0])))}这里Array.prototype.slice.apply(arguments,[0])引用到return函数执行时传递的一系列参数,所以从第一个参数[0]开始,前面的args=slice.apply(arguments,[1])指的是执行testBind方法传递的参数当时,所以从第二个[1]开始,两者是根本不同的,不能混为一谈。只有两者合并后才是返回函数的完整参数。于是就有了下面的实现Function.prototype.testBind=function(that){var_this=this,slice=Array.prototype.slice,args=slice.apply(arguments,[1]);returnfunction(){return_this.apply(that,args.concat(Array.prototype.slice.apply(arguments,[0])))}}testvartest=function(a,b){console.log('scopebinding'+this.value)console.log('testBind参数传递'+a.value2)console.log('调用参数传递'+b)}varobj={value:'ok'}varfun_new=test.testBind(obj,{value2:'alsook'})fun_new('hellobind')//scopebindingok//testBind传参alsoook//调用传参hellobind上述两种传参方式中,bind的优先级较高,从args.concat(Array.prototype.slice.apply(arguments,[0]))出来了,bind的参数在数组前面。官方原型链文档中有一句话:Aboundfunctionmayalsobeconstructedusingthenewoperator:doingsoactsthoughthetargetfunctionhadinsteadbeenconstructed。提供的此值将被忽略,而前置参数将提供给模拟函数。说明被绑定的函数被new实例化后,需要继承原函数的原型链方法,绑定过程中提供的this会被忽略(继承原函数的this对象),但参数仍然会是用过的。这里需要一个中转函数将原型链向下传递fNOP=function(){}//创建一个中转函数fNOP.prototype=this.prototype;xx.prototype=newfNOP()修改如下Function.prototype.testBind=function(that){var_this=this,slice=Array.prototype.slice,args=slice.apply(arguments,[1]),fNOP=function(){},//所以调用后有name属性值官方绑定方法'bound'bound=function(){return_this.apply(that,args.concat(Array.prototype.slice.apply(arguments,[0])))}fNOP.prototype=_this.prototype;bound.prototype=newfNOP();returnbound;}而绑定方法的第一个参数this可以省略。bind后有两种情况需要直接调用方法varf=function(){console.log('如果不传则默认为'+this)};f.bind()()//默认为Windowif不传递,所以直接调用绑定方法时apply(that,建议改成apply(即||用new实例化绑定方法容易混淆,关键是搞清楚标准是什么bind方法在new的时候做了什么,然后就可以清楚的实现了,这里要看new方法做了什么操作,比如vara=newb()创建一个空对象a={},这个变量引用指向这个空对象a继承了实例化函数的原型:a.__proto__=b.prototype实例化方法b的this对象的属性和方法将被添加到这个新this引用的对象:的属性和方法b添加到a中新创建的对象都是通过这个引用的:b.call(a)从上面我们可以知道,如果是varafter_new=newbindFun();由于此行为是将原始函数视为构造函数,那么最终实例化后的对象this需要继承原函数,这里的bindFun目前是function(){return_this.apply(that||window,args.concat(Array.prototype.slice.apply(arguments,[0])))}这里apply的范围就是||的界限window,在执行testBind()时已经固定,没有继承原函数的this对象,不符合我们的要求。我们需要根据apply的特点来解决这个问题:在子构造函数中,可以通过调用父构造函数的`apply/call`方法来实现继承,比如functionProduct(name,price){this.name=name;这。price=price;if(price<0){throwRangeError('Cannotcreateproduct'+this.name+'withanegativeprice');}}functionFood(name,price){Product.call(this,name,price);this.category='food';}//相当于(其实就是把Product放到Food里面执行一次)functionFood(name,price){this.name=name;this.price=price;if(price<0){throwRangeError('Cannotcreateproduct'+this.name+'withanegativeprice');}this.category='food';}所以在创建新实例的时候,实时应用新的this对象,继承原函数的this对象实现new方法里面apply(that||window,//修改为如果是new,new之后的作用域需要绑定,this指向新的实例对象apply(isNew? this:that||window,==>Function.prototype.testBind=function(that){var_this=this,slice=数组。prototype.slice,args=slice.apply(arguments,[1]),fNOP=function(){},//所以在调用官方的bind方法后,有一个name属性值为'bound'bound=function(){return_this。应用(是新的吗? this:that||window,args.concat(Array.prototype.slice.apply(arguments,[0])))}fNOP.prototype=_this.prototype;bound.prototype=newfNOP();returnbound;}这里的isNew是为了区分bindFun是直接调用还是new调用后调用。从原型链的继承关系可以知道bindFun属于after_new的父类,所以after_newinstanceofbindFun为真,bindFun.prototype=newfNOP()原型继承;所以fNOP也是after_new的父类,after_newinstanceoffNOP为true最终结果为Function.prototype.testBind=function(that){var_this=this,slice=Array.prototype.slice,args=slice.apply(arguments,[1]),fNOP=function(){},bound=function(){//这里指的是调用return_this.apply(thisinstanceoffNOP? this:that||window,args.concat(Array.prototype.slice.apply(arguments,[0])))}fNOP.prototype=_this.prototype;bound.prototype=newfNOP();returnbound;}我看到thisinstanceoffNOP&&that?this:that||window,我个人觉得这个有点不对,如果绑定的时候不传参数,那就是空的,无论如何,它只能绑定到窗口范围。以上是我个人的看法。如有不妥,请大家指点,谢谢!
