underscore链式操作
涉及函数:_.chain(obj) === _(obj).chain()
实例:
// 非 OOP 链式调用
_.chain([1, 2, 3])
.map(function(a) {return a;})
.reverse()
.value(); // [3, 2, 1]
// OOP 链式调用
_([1, 2, 3])
.chain()
.map(function(a){return a;})
.first()
.value(); // 1
解析:
以上两种方式都可以达到链式调用的目的。在underscore内部实现中,这两种方式实现都是用了OOP的实现方法,具体来看实现函数:
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};
_.chain(obj)
通过调用chain()
方法将_(obj)
先转化为_
的实例对象,然后将实例对象添加_chain
属性,返回该实例对象。
【注:对于_(obj)
不熟悉的可以移步
underscore中_是弄啥的】
而对于_(obj).chain()
来说,首先通过_(obj)
创建_
的实例对象,然后该实例调用_
对象原型中的chain()
方法【该方法通过_.mixin()
方法混入到_.prototype
中】详见:underscore中OOP思想—实例对象方法调用实现机制
_.prototype['chain']
方法最后一步:
// _.mixin()方法中_.prototype[name] = function() {...}最后返回
return chainResult(this, func.apply(_, args));
// chainResult实现函数
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
chainResult(this, func.apply(_, args))
方法中this
是_
对象,func.apply(_, args)
调用_.chain()
方法返回传入对象的实例,在上面例子中就是[1, 2, 3]
对象的实例对象,并给该对象添加_chain
属性,返回该实例对象。此时调用为chain(_对象, [1, 2, 3]添加了_chain属性的实例对象)
,_._chain === undefined
所以直接返回添加了_chain
属性的[1, 2, 3]
对象实例。
到这里是不是发现了什么?!Yes,_.chain(obj)
和_(obj).chain()
回到了同一起跑线!
最后再梳理一下其中逻辑:chainResult实现函数通过传入的obj实例和obj对象,判断如果obj实例中含有chain属性则继续调用(obj).chain()生成具有_chain属性的obj实例对象;如果没有,则说明obj本身已经有了chain属性【因为调用该函数的都是在.prototype[XXX]中,该函数的执行最后必然会调用上面说的chainResult方法,func.apply(this, args)最终返回的一定是带有_chain属性的实例对象】,直接返回obj即可。