很早就想研究underscore源码了,虽然underscore.js这个库有些过期了,可是我仍是想学习一下库的架构,函数式编程以及经常使用方法的编写这些方面的内容,又刚好没什么其它要研究的了,因此就告终研究underscore源码这一心愿吧。html
underscore.js源码研究(1)
underscore.js源码研究(2)
underscore.js源码研究(3)
underscore.js源码研究(4)
underscore.js源码研究(5)
underscore.js源码研究(6)
underscore.js源码研究(7)
underscore.js源码研究(8)git
参考资料:underscore.js官方注释,undersercore 源码分析,undersercore 源码分析 segmentfault编程
对于一个对象的方法,咱们能够在方法里面return this来使它支持链式调用。若是不修改原方法的源码,咱们能够经过下面的函数使它支持链式调用。segmentfault
//其中obj是一个对象,functions是对象的方法名数组 function chained(obj, functions) { //因为functions是一个数组,因此可使用foreach functions.forEach((funcName) => { const func = obj[funcName]; //修改原方法 obj[funcName] = function() { func.apply(this, arguments); return this; } }) }
示例以下:api
let speaker = { haha() { console.log('haha'); }, yaya() { console.log('yaya'); }, lala() { console.log('lala'); } } chained(speaker, ['haha', 'lala', 'yaya']); speaker.haha().yaya().haha().lala().yaya();
输出以下:数组
haha yaya haha lala yaya
underscore.js里面的链式调用与上面的稍微有些不一样。架构
它的机制是这样的:app
下面来具体看看是怎么运做的:函数式编程
首先,利用下面的函数,能够获得一个数组,这个数组里面全是挂载到_下面的方法名字。函数
_function = _.methods = function(obj) { var names = []; for( var key in obj) { if(_.isFunction(obj[key])) names.push(key); } return names.sort(); };
而后咱们对每个方法名字,把它挂载到_.prototype下面:
_.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return chainResult(this, func.apply(_, args)); }; }); }; _.mixin(_);
从上面能够看到,在挂载的时候返回一个chainResult方法处理过的东西。而args就等于原对象+参数1+参数2+...组成的数组,因此func.apply(_, args)
就是_[func](原对象,参数1,参数2,...)
处理后的结果。
再来看看chainResult是怎么样的:
var chainResult = function(instance, obj){ return instance._chain ? _(obj).chain() : obj; };
它判断若是原对象可以链式调用,那么处理后的结果obj也可以链式调用。怎么让结果也能链式调用呢?答案是使用_.chain方法:
_chain = function(obj) { var instance = _(obj); instance._chain = true; return instance; }
这个方法和咱们最开始的chained方法差很少,可是它会把原对象转化为underscore对象,而且这个对象的**_chain属性为真**,即它可以被链式调用。
因此若是咱们要在underscore.js中实现链式调用的话,直接用chain方法便可,实例以下:
_([1,2]).push(3).push(5) //输出4,并非咱们想要的 _([1,2]).chain().push(3).push(5).value() //输出[1, 2, 3, 5],正是咱们想要的
能够看到,咱们上面用到了value()函数,它可以中断链式调用,并不返回指针而是返回值,代码以下:
_.prototype.value = function() { return this._wrapped; }
等等,_wrapped又是什么?它是生成underscore对象前的原对象,看下面的代码:
var _ = function(obj) { if(obj instanceof _) return obj; if(!(this instanceof _)) return new _(obj); this._wrapped = obj; }
也就是说,在使用_生成underscore对象的时候,把原对象储存在**_wrapped属性**里面了,因此_wrapped就是原对象。