前言这是underscore.js源码解析的第五篇。如果您对本系列感兴趣,请点击underscore-analysis/watch,随时查看动态更新。js中的事情就是从这个开始的。你是不是常常觉得自己无法控制和知道?对于初学者来说,这就像一个回调地狱,神秘莫测。但是通过原生js中的bind方法,我们可以将绑定函数的this作用域显示出来,而不用担心它会不会在运行时发生变化,达不到我们的预期。当然,underscore中的bind也模仿了它的作用,也可以达到类似的效果。Bind简述我们从mdn上的介绍来回顾一下bind的使用。bind方法创建一个新函数,其this关键字在调用时设置为提供的值。语法fun.bind(thisArg[,arg1[,arg2[,...]]])简单看一下这几个参数的含义运行时,用new操作符调用绑定函数时该参数无效。arg1,arg2,...当调用绑定函数时,这些参数将在实际参数之前传递给绑定方法。绑定这个范围的例子window.name='windowName'letobj={name:'qianlongo',showName(){console.log(this.name)}}obj.showName()//qianlongoletshowName=obj.showNameshowName()//windowNameletbindShowName=obj.showName.bind(obj)bindShowName()//qianlongo通过上面的简单例子,我们知道第一个参数的作用是在函数执行时绑定this指向第二个参数runningLetsum=(num1,num2)=>{console.log(num1+num2)}letbindSum=sum.bind(null,1)bindSum(2)//3bind可以让一个函数有一个预设的初始参数。这些参数(如果有的话)后面跟着this(或其他对象)作为bind的第二个参数,之后它们会被插入到目标函数的参数列表的开头,传递给被绑定函数的参数后面会跟着在他们后面。参数的使用基本了解了,下面我们来看看如何使用new调用bind之后的函数。functionPerson(name,sex){console.log(this)//Person{}this.name=namethis.sex=sex}letobj={age:100}letbindPerson=Person.bind(obj,'qianlongo')letp=newbindPerson('boy')console.log(p)//Person{name:"qianlongo",sex:"boy"}有没有发现bindPerson里面的this不再是bind的第一个参数obj了,此时obj不再有效。其实bind的使用是有一定限制的,在一些低版本的浏览器中是不可用的。你想看看如何在下划线中实现一个兼容良好的绑定吗!!!来下划线的bind实现源码_.bind=function(func,context){//如果原生支持bind函数,就使用原生的,并传入相应的参数if(nativeBind&&func.bind===nativeBind)返回nativeBind.apply(func,slice.call(arguments,1));//如果传入的func不是函数类型则抛出异常if(!_.isFunction(func))thrownewTypeError('Bindmustbecalledonafunction');//保存第三个参数后的值,见下executeBoundvarargs=slice.call(arguments,2);varbound=function(){returnexecuteBound(func,bound,context,this,args.concat(slice.call(arguments)));};返回绑定;};executeBoundimplementationvarexecuteBound=function(sourceFunc,boundFunc,context,callingContext,args){//如果调用方法不是newfunc的形式,直接调用sourceFunc并给出相应的参数if(!(callingContextinstanceofboundFunc))returnsourceFunc.apply(上下文,参数);//处理新调用的形式varself=baseCreate(sourceFunc.prototype);varresult=sourceFunc.apply(self,args);如果(_.isObject(result))返回结果;返回自我;};上面的源代码已经做了相应的注释。下面重点介绍executeBound的实现。首先我们来看看这些参数代表什么。sourceFunc:原函数,待绑定函数boundFunc:绑定函数context:bound设置后该函数指向的上下文,我们先看第一句if(!(callingContextinstanceofboundFunc))returnsourceFunc.apply(context,args);这句话是判断绑定函数是用new关键字调用还是普通函数调用。例如,functionPerson(){if(!(thisinstanceofPerson)){returnconsole.log('非新调用方法')}console.log('新调用方法')}Person()//非-newcallingmethodnewPerson()//newcallingmethod所以如果你想自己写如果构造函数无论是new还是不new都有效,你可以使用下面的代码functionPerson(name,sex){if(!(thisinstanceofPerson)){returnnewPerson(name,sex)}this.name=namethis.sex=sex}newPerson('qianlongo','boy')//Person{name:"qianlongo",sex:"boy"}Person('qianlongo','boy')//Person{name:"qianlongo",sex:"boy"}我们回到executeBound的分析if(!(callingContextinstanceofboundFunc))returnsourceFunc.apply(上下文,参数);callingContext是绑定函数的this作用域,而boundFunc是绑定函数,那么通过thisif判断,当当以非new形式调用时,直接用apply处理即可,但如果用new调用呢?我们看下面的代码,别看短短的四行代码,里面的知识点还真不少呢!//这里得到的是一个空对象,继承自原函数的原型链。prototypevarself=baseCreate(sourceFunc.prototype);//构造函数执行后的返回值//一般没有返回值,也就是undefined//但是有时候写构造函数的时候,会返回一个objvarresult=sourceFunc.apply(self,args);//所以判断result是否为对象,如果是,则返回构造函数返回的objectif(_.isObject(result))returnresult;//如果返回的对象是不显示,执行原函数returnself后返回实例;好吧,在这里,我有一个问题,baseCreate是什么鬼?varCtor=function(){};varbaseCreate=function(prototype){//如果原型不是对象类型,直接返回空对象if(!_.isObject(prototype))return{};//如果原生支持创建,则使用Nativeif(nativeCreate)returnnativeCreate(prototype);//将原型赋值给Ctor构造函数的原型Ctor.prototype=prototype;//创建一个Ctor实例对象varresult=newCtor;//为了下次使用,设置清除原型Ctor.prototype=null;//最后返回实例返回结果;};是不是这么简单,就是把原来的Object.create实现出来,做一些继承。完结篇很短,只知道如何实现一个nativebind。如果你对apply,call,this感兴趣,请查看js中call,apply,bind的那些事【this-好不容易说出我爱你】(https://qianlongo.github.io/2...)
