get first、set all策略------QWrap的Function.mul变换 之一
get first/set all策略由jquery推出以来,很深入人心,很多享受他便利的使用者,甚至都没意识到还有这个策略的存在----大道自然啊。
它是个什么样的策略呢?
代码1(代码仅为示意,不是可执行的):
W([el1,el2]).setValue(1),
很显然,这句话会造成el1.value=1,el2.value=1。
即,操作对所有元素都有效。
对于set操作,各个框架都是一样的策略:set all。
代码2:
W([el1,el2]).getValue() 返回的是什么呢?
A: 是el1.value
B: 还是[el1.value,el2.value] ?
这两个结果,A代表的是get first;B代表的是get all。
Jquery选择了A策略,即get first,这样看起来更贴近用户的需求。
而YUI3的NodeList选用了B策略,即get all,或许他认为这样看起来语义更严谨,也或许他在实现上有顾虑。
QWrap可以无所顾虑的来决定:默认是get first策略,也可以配置成get all策略。
这是因为QWrap的思路就是:“代码”与“使用”相分离。
即,代码是一样的,但使用的是经过变换后的接口或方法。
get first、set all策略,主要通过FunctionH.mul变换来实现。
看一下mul是个什么样的方法:
* @method mul
* @static
* @param {function} func
* @param {boolean} recursive 是否递归
* @param {boolean} getFirst 是否只是getFirst
* @return {Object} 已集化的函数
*/
function mul(func, getFirst){
//get First
if(getFirst){
//以下代码,递归回溯寻找第一个有效元素,例如[[[],[]],[[],[el]]] 会找到el,再去执行func
//注意:诸如[null]、[undefined]这种会被当做非有效元素而忽略
function findFirst(list){
if(!(list instanceof Array)) {
return list;
}
for(var i=0;i<list.length;i++){
var firstOne = findFirst(list[i]);
if(null != firstOne) return firstOne;
}
};
return function(){
var firstOne = findFirst(arguments[0]);
if(firstOne){
var args = [].slice.call(arguments,0);
args[0] = firstOne;
return func.apply(this,args);
}
};
}
//get All
var newFunc = function(){
var list = arguments[0];
if(list instanceof Array){
var ret = [];
var moreArgs = [].slice.call(arguments,0);
for(var i = 0, len = list.length; i < len; i++){
moreArgs[0]=list[i];
var r = newFunc.apply(this, moreArgs);
ret.push(r);
}
return ret;
}else{
return func.apply(this, arguments);
}
}
return newFunc;
};
mul是multiple的缩写,它的作用是让一个函数的第一个参数可以是数组。
原生的getValue/setValue方法不必关心第一个参数是数组的情况。
如以下的NodeH。(H是Helper的缩写,Helper规范约定其所有方法都是静态方法,并且第一个参数有同样的针对性。参见QWrap的Helper规范)
getValue: function(el){
return el.value;
},
setValue: function(el,value){
el.value=value;
},
getStyle: function(el,prop){
return el.style[prop];
},
setStyle: function(el,prop,value){
el.style[prop]=value;
}
};
这里,我们需要一个NodeW的对象,(W是Wrap的缩写,为了保护core的核心,而在外面加一层Wrap,详细参见QWrap设计篇之Wrap。jquery、YUI.NodeList、YUI.Node都可以理解成一个Wrap)
this.core=core;
};
以下变换需要用到另一个变换:methodize:
return function(){
var ret = func.apply(null,[this.core].concat([].slice.call(arguments)));
return chain?this:ret;
}
};
好了,以下几句话就可以做到NodeW用起来就像是一个jquery对象了
NodeW.prototype.getStyle=methodize(mul(NodeH.getStyle,true));
NodeW.prototype.setValue=methodize(mul(NodeH.setValue),true);
NodeW.prototype.setStyle=methodize(mul(NodeH.setStyle),true);
之后,我们就可以像jquery一样来这样的调用了。
var w=new NodeW([els[0],els[1]]);
var val=w.setStyle('color','red').setValue(1).getValue();
alert(val);
全部代码如下:
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
</HEAD>
<BODY>
<input id="test1">
<input id="test2">
<input type=button value="运行test" onclick="test();">
</BODY>
<script>
/** 对函数进行集化,使其第一个参数可以处理数组
* @method mul
* @static
* @param {function} func
* @param {boolean} recursive 是否递归
* @param {boolean} getFirst 是否只是getFirst
* @return {Object} 已集化的函数
*/
function mul(func, getFirst){
//get First
if(getFirst){
//以下代码,递归回溯寻找第一个有效元素,例如[[[],[]],[[],[el]]] 会找到el,再去执行func
//注意:诸如[null]、[undefined]这种会被当做非有效元素而忽略
function findFirst(list){
if(!(list instanceof Array)) {
return list;
}
for(var i=0;i<list.length;i++){
var firstOne = findFirst(list[i]);
if(null != firstOne) return firstOne;
}
};
return function(){
var firstOne = findFirst(arguments[0]);
if(firstOne){
var args = [].slice.call(arguments,0);
args[0] = firstOne;
return func.apply(this,args);
}
};
}
//get All
var newFunc = function(){
var list = arguments[0];
if(list instanceof Array){
var ret = [];
var moreArgs = [].slice.call(arguments,0);
for(var i = 0, len = list.length; i < len; i++){
moreArgs[0]=list[i];
var r = newFunc.apply(this, moreArgs);
ret.push(r);
}
return ret;
}else{
return func.apply(this, arguments);
}
}
return newFunc;
};
function methodize(func,chain){
return function(){
var ret = func.apply(null,[this.core].concat([].slice.call(arguments)));
return chain?this:ret;
}
};
var NodeH={
getValue: function(el){
return el.value;
},
setValue: function(el,value){
el.value=value;
},
getStyle: function(el,prop){
return el.style[prop];
},
setStyle: function(el,prop,value){
el.style[prop]=value;
}
};
function NodeW(core){
this.core=core;
};
NodeW.prototype.getValue=methodize(mul(NodeH.getValue,true));
NodeW.prototype.getStyle=methodize(mul(NodeH.getStyle,true));
NodeW.prototype.setValue=methodize(mul(NodeH.setValue),true);
NodeW.prototype.setStyle=methodize(mul(NodeH.setStyle),true);
function test(){
var els=document.getElementsByTagName('input');
var w=new NodeW([els[0],els[1]]);
var val=w.setStyle('color','red').setValue(1).getValue();
alert(val);
}
</script>
</HTML>
说明:
mul还有其它参数、等等更多内容,可以参见QWrap的QW.FunctionH,相信对于JS开发者会有很多帮助。
QWrap 参见:http://github.com/wedteam/qwrap
另外,LC同学也写过一篇介绍QWrap骨骼的文章,参见:http://archive.cnblogs.com/a/1926941/
月影也写过一篇:“浅说QWrap的核心机制” http://bbs.51js.com/viewthread.php?tid=88347