前言楼主的《Underscore源码解读系列》underscore-解析终于告一段落了。关注一下时间线,会发现主持人最近加快了解读速度。十一月是一个多事的季节。宿主最近被卷入了很多事情,精神疲惫,身心俱疲。他也想尽快结束这个系列赛,但他心里只有一件事。这篇文章预计是解读系列的倒数第二篇,最后一篇显然是一个大总结。楼主的Underscore系列解读完整版地址https://github.com/hanzichi/u...常规调用之前写的文章,大部分着重在具体方法,具体知识细节,部分读者留言建议楼主说一下整个Architecture,这个是必须要说的,但是楼主已经安排在了***,也就是这篇文章,因为楼主觉得在理解上没有太大问题没有掌握整体架构的具体方法。大多数时候Underscore的调用形式是_.funcName(xx,xx),也是文档中的调用方式。_.each([1,2,3],警报);最简单的实现,我们可以把_看成一个??简单的对象:var_={};_.each=function(){//...};在JavaScript中,一切都是对象。其实源码中的_变量是一个方法:var_=function(obj){if(objinstanceof_)returnobj;if(!(thisinstanceof_))returnnew_(obj);this._wrapped=obj;};这是为什么呢一个方法?让我们接下来看看。OOPUnderscore支持OOP形式的调用:_([1,2,3]).each(alert);这其实是一个非常经典的“无新构造”,_其实是一个构造函数,_([1,2,3])是一个对象实例,它有一个值为[1,2,3]的_wrapped属性。实例需要调用各个方法,没有这个方法,所以应该从原型链来,也就是说_.prototype上应该有这个方法,那么,这个方法是怎么挂载的呢?方法挂载现在我们明确了以下两点:_是一个函数(支持不调用new的构造函数)_的属性有很多方法,比如_.each,_.template等。我们的目标是让构造的实例_也调用这些方法。仔细想想,其实也不难。我们可以遍历_上的属性,如果属性值类型是一个函数,那么就把函数挂在_的原型链上。源码中用来完成这个的_.mixin方法://AddyourowncustomfunctionstotheUnderscoreobject.//你可以将自己的方法扩展到underscore函数库中//obj参数必须是一个对象(JavaScript中的一切都是对象)//而你自己的方法定义在obj的属性上//比如obj.myFunc=function(){...}//形如{myFunc:function(){}}//之后就可以作为如下:_.myFunc(..)或OOP_(..).myFunc(..)_.mixin=function(obj){//遍历obj的key,将方法挂载在Underscore上//其实就是浅拷贝_.prototype_的方法。each(_.functions(obj),function(name){//直接把方法挂载到_[name]上//调用类似_.myFunc([1,2,3],..)varfunc=_[name]=obj[name];//浅拷贝//将name方法挂载到_对象的原型链上,使其可以OOP调用_.prototype[name]=function(){//***参数可变参数=[this._wrapped];//arguments是name方法需要的其他参数push.apply(args,arguments);//执行func方法//支持链式操作returnresult(this,func.apply(_,args));};});};//AddalloftheUnderscorefunctionstothewrapperobject.//将之前定义的underscore方法添加到wrapped对象中//即添加到_.prototype中//使underscore支持面向对象调用_.mixin(_);_.mixin方法可以在Underscore库中添加自定义方法:_.mixin({capitalize:function(string){returnstring.charAt(0).toUpperCase()+string.substring(1).toLowerCase();}});_("fabio").capitalize();=>"Fabio"同时,Underscore还添加了一些Array原生方法://AddallmutatorArrayfunctionstothewrapper.//将Array原型链上的所有方法添加到underscore_.each(['pop','push','reverse','shift','sort','splice','unshift'],function(name){varmethod=ArrayProto[name];_.prototype[name]=function(){varobj=this._wrapped;method.apply(obj,arguments);if((name==='shift'||name==='splice')&&obj.length===0)deleteobj[0];//支持链式操作returnresult(this,obj);};});//AddallaccessorArrayfunctionstothewrapper.//将concat、join、slice等数组原生方法添加到Underscore_.each(['concat','join','slice'],function(name){varmethod=ArrayProto[name];_.prototype[name]=function(){returnresult(this,method.apply(this._wrapped,arguments));};});链式调用Underscore也支持链式调用://非OOP链式调用_.chain([1,2,3]).map(function(a){returna*2;}).reverse().value();//[6,4,2]//OOP链调用_([1,2,3]).chain().map(function(a){returna*2;}).first().value();//2乍一看好像有OOP链式调用和非OOP两种形式其实只是一种。_.chain([1,2,3])和_([1,2,3]).chain()的结果是一样的。如何实现呢?让我们深入了解链式方法。_.chain=function(obj){_.chain=function(obj){//是否调用OOP,都会转为OOP形式//并在其中添加一个_chain属性varinstance=_(obj)newconstructionobject;//标记是否使用链式操作instance._chain=true;//返回OOP对象//可以看到实例对象多了一个_chain属性//其他结果同直接_(obj)返回实例;};让我们看看_.chain([1,2,3])的结果并将参数代入函数。其实就是在没有new的情况下构造参数,然后返回实例,只不过实例多了一个_chain属性,其他和direct_([1,2,3])一模一样。我们看一下_([1,2,3]).chain(),_([1,2,3])返回一个构造好的实例,里面有一个chain方法,调用方法,给实例加上_chain属性,并返回实例对象。所以,两者的效果是一致的,结果转为OOP。说了这么多,好像还没说正题呢。它是如何“连锁”下来的?我们以下面的代码为例:_([1,2,3]).chain().map(function(a){returna*2;}).first().value();//2当调用了map方法,实际上可能会有一个返回值。我们看_.mixin源码://执行func方法//支持链式操作returnresult(this,func.apply(_,args));result是一个重要的内部辅助函数(Helperfunction)://Helperfunctiontocontinuechainingintermediateresults。//一个辅助方法(Helperfunction)varresult=function(instance,obj){//如果需要链式操作,就在obj上运行chain方法,这样后续的链式操作才能继续//如果不需要,直接返回objreturninstance。_chain?_(obj).chain():obj;};如果需要进行链式操作(实例将具有_chain属性),则在操作结果上调用链式函数,以便它可以继续进行链式调用。总结一下Underscore的整体结构,或者说基本的实现大概是这样,代码部分就到这里,下一个系列的解读是最后一个,说说这期间(快半年)的一些心得,如果你不会没钱,可以hold住个人领域!
