chain
_.chain(lyrics)
.map(function(line) { return line.words.split(' '); })
.flatten()
.reduce(function(counts, word) {
counts[word] = (counts[word] || 0) + 1;
return counts;
}, {})
.value();
Underscore
支持链式调用;不过要求函数链的开头要用chain
函数生成封装对象。
看看chain
到底做了啥。
_.chain = function (obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};
chain
生成了返回了一个underscore
实例
var _ = function (obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
我们看到underscore
实例就是一个挂载了_wrapper
的对象。
再接着,因为_
是个构造函数,所有方法都是挂载在构造函数上的。
Underscore
提供了mixin
方法为Underscore
对象进行扩展,我们看看。
_.mixin = function (obj) {
_.each(_.functions(obj), function (name) {
// 对传入的obj,遍历它所有的方法,先挂载在构造函数上
var func = _[name] = obj[name];
// 并且所有的方法都会在原型上重写;
_.prototype[name] = function () {
var args = [this._wrapped];
push.apply(args, arguments);
return result(this, func.apply(_, args));
};
});
};
_.mixin(_);
Underscore
通过mixin
将原来挂载在构造函数上所有的方法,全部在_
的原型上重写,这次重写,直接指定了underscore
所有方法的第一个参数为this._wrapped
,也就是构造函数携带的参数。
然后看看最后为了链式调用返回的result
函数。明显支持这种“不带”第一个参数的写法是只能写在underscore
实例上的。所以在原型上重写的方法,最后返回的仍然是underscore
实例。
var result = function (instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
也就是说,underscore
的链式调用一直返回的是一个_
的实例,_
的构造方法不仅挂载了全部underscore
的方法,并且在原型上重写了这些方法,使得_
的实例可以不用传递第一个参数而进行链式调用。