QWrap简介之:NodeW Node包装

专注于dom的jquery广受欢迎,说明了dom的节点操作在js应用中的沉重份量。
节点操作需求多样,如果只局限于节点原型扩展,会阻碍重重束手束脚。针对节点的包装“NodeW”的出现,把节点的操作带到了一个没有边际的新天地。
前文也提到,jquery的实质,就是一个nodelist的包装。

我们先假设有一个针对Node的Helper:
var NodeH = {
setStyle:
function(el, attribute, value) {
el.style[attribute]
= value;
},
getStyle:
function(el, attribute) {
return el.style[attribute];
}
};

下文讲述NodeW的进化过程:从最简单形式进化成强大成熟易用的NodeW。

第一阶段:最简形式-----只包装单个元素
NodeW的最简形式:
function NodeW(core) {
this.core=core;
}

它有两个对应的原型方法:
NodeW.prototype.setStyle = function(attribute, value) {
NodeH.setStyle(
this.core, attribute, value);
};
NodeW.prototype.getStyle
= function(attribute) {
return NodeH.getStyle(this.core, attribute);
};


那么我们就可以这样用了:
var w = new NodeW(document.body);
w.setStyle(
'color', 'red');

其效果相当于document.body.style.color='red';
其中,“var w = new NodeW(document.body)”是一个装箱过程;“w.setStyle('color', 'red')”调用的是箱壳的原型方法,不过可以看到,其代码里操作的是“this.core”,即核。


第二阶段:支持数组
很多情况下,我们包装的是一个节点数组。这就得要求:除了要有能力对单个节点包装,还必须要对节点数组包装
方案有三种:
yui3方案:单个包装是Node,多个包装是NodeList,两个不同的类。
jquery方案:jQuery对象反正是一个ArrayLike,包装单个与多个都是以数组的形式来。
qwrap方案:都是NodeW,不过,它的core核有时是数组,有时是节点。
对于数组来说,setStyle的意义很明显,是对每一个节点都setStyle;可是,getStyle,也是对每一个元素都getStyle吗?
yui3的NodeList的做法,是每一个都getStyle,这样可能从理论上要严谨一些,可是,事实上绝大多数获取的结果,我们都希望是一个值,而不是值的数组。
jquery的做法:getStyle,只对第一个元素进行getStyle,并且返回这个值。叫做“set allget one”策略。
qwrap因为有了函数变换的概念,能够灵活配置,最后的效果是:“set allget oneget all
简而言之,就是如下的情况:
function NodeW(core) {
this.core=core;
}
NodeW.prototype.setStyle
= function(attribute, value) {
var core=this.core;
if(core instanceof Array){
for(var i=0; i<core.length; i++){
NodeH.setStyle(core[i], attribute, value);
}
}
else NodeH.setStyle(this.core, attribute, value);
};
NodeW.prototype.getStyle
= function(attribute) {
var core=this.core;
if(core instanceof Array){
return NodeH.setStyle(core[0], attribute);
}
else return NodeH.getStyle(this.core, attribute);
};
NodeW.prototype.getStyleAll
= function(attribute) {
var core=this.core;
if(core instanceof Array){
var ret=[];
for(var i=0; i<core.length; i++){
ret.push(NodeH.getStyle(core[i], attribute);
}
return ret;
}
};

会不会觉得代码太多?
----那是因为这里没用到函数变换的利器。
上面的那段代码,可以改成这段:
function NodeW(core) {
this.core=core;
}
NodeW.prototype.setStyle
= FunctionH.methodize(FunctionH.mul(NodeH.setStyle), 'core');
NodeW.prototype.getStyle
= FunctionH.methodize(FunctionH.mul(NodeH.getStyle,1), 'core');
NodeW.prototype.getStyleAll
= FunctionH.methodize(FunctionH.mul(NodeH.getStyle), 'core');

也就是说,这些原型方法,都是那些静态方法的变种,只需要一些简单的变化就可以得到了。----这些变换在前面的FunctionH里已介绍过。

第三阶段:支持selector
在第二阶段的基础上,加上selector的支持就很简单了---只需引用一个Selector的类。
只需改下NodeW的代码即可,如下:
function NodeW(core) {
if(typeof core == 'string') core=Selector.query(core);
this.core=core;
}

例如:
//<body><div></div><div></div></body>
var w=new NodeW('div');
w.setStyle(
'width', '200px'); //每一个都设置width
alert(w.getStyle('width')); //显示'200px';
alert(w.getStyleAll('width')); //数组['200px','200px'];


第四阶段:支持html
在第三阶段的础上,希望参数可以是html字符串。例如<p>P1</p><p>P2</p>。
好的,那也很简单,即:如果是html字符串的话,则自动创建相应的元素就可以了。
因为selector与html都是字符串,所以,在不添加参数的情况下,需要区分它们。
QWrap采用的区分方式是:第一个字符是'<'则当作html。对于第一个字符不是'<'还是想把它当html,不可以用这种用法。
还是很简单,示意代码如下:
function NodeW(core) {
if (typeof core == 'string')) {
if (/^</.test(core)) { //用法:html
var list = Dom.create(core, true).childNodes,//创建碎片,并得到childNodes
els = [];
for (var i = 0, elI; elI = list[i]; i++) {
els[i]
= elI;
}
core
=els;
}
else { //用法:selector;
core=Selector.query(core);
}
}
this.core=core;
}

之后,我们就可以这样用了:
var w=new NodeW('<div></div>');
w.setStyle(
'width','200px');
alert(w.getStyle(
'width'));

第五阶段:支持链式调用。
有时候,需要连续几个设置,最后又获取,这种写法可能有点生硬。
var w=new NodeW('<div></div>');
w.setStyle(
'width','200px');
w.setStyle(
'color','red');
alert(w.getStyle(
'width'));

自jquery起,就吹起了一种链式调用的风潮:
var w=new NodeW('<div></div>');
alert(w.setStyle(
'width','200px').setStyle('color','red').getStyle('width'));

之前,静态函数NodeH.setStyle没有返回值,我们这里希望的NodeW.prototype.setStyle需要的是返回this。
这就牵涉到一个返回值的定制。
哦哟哟,有点麻烦了!
不用担心,我们在HelperH里定制了几套看起来有点麻烦的变换。
因为这个NodeH是个标准的Helper,满足“纯洁、静态、有针对性”三个特性,所以,我们可以直接借助HelperH,其代码就是:
function NodeW(core) {
if (typeof core == 'string')) {
if (/^</.test(core)) { //用法:html
var list = Dom.create(core, true).childNodes,//创建碎片,并得到childNodes
els = [];
for (var i = 0, elI; elI = list[i]; i++) {
els[i]
= elI;
}
core
=els;
}
else { //用法:selector;
core=Selector.query(core);
}
}
this.core=core;
}
(
function(){
var config = {
getStyle:
'getter_first_all', //标明这个方法是get first;同时也生成一个getStyleAll的方法,作为get all。
setStyle: 'operator' //标明这个方法是一个操作方法,操作方法都可以链式。
};
var helper = HelperH.mul(NodeH, config); //支持第一个参数为array
var pro = HelperH.methodize(helper, 'core'); //方法化
pro = HelperH.rwrap(pro, NodeW, config); //
ObjectH.mix(NodeW.prototype, pro);
}());

好了,我们就可以链式调用了
var w=new NodeW('div');
alert(w.setStyle('width','200px').setStyle('color','red').getStyle('width'));
当然,也可以这样写:
alert(new NodeW('div').setStyle('width','200px').setStyle('color','red').getStyle('width'));

第六阶段:去“new ”
这写法:alert(new NodeW('div').setStyle('width','200px').setStyle('color','red').getStyle('width'));
可能会有很多同学觉得不爽,为啥还要那个“new ”。
好的,我们也可以去掉,将代码改成如下即可。
function NodeW(core) {
if(!(this instanceof NodeW)) return new NodeW(core);// 静态调用方式,转化成构造器调用方式。
if (typeof core == 'string')) {
if (/^</.test(core)) { //用法:html
var list = Dom.create(core, true).childNodes,//创建碎片,并得到childNodes
els = [];
for (var i = 0, elI; elI = list[i]; i++) {
els[i]
= elI;
}
core
=els;
}
else { //用法:selector;
core=Selector.query(core);
}
}
this.core=core;
}


第七阶段:NodeW就该是ArrayLike,希望用W('div')[0]得到第个div对象。
第八阶段:数组相关操作,如W('div').first().setStyle('color','red');
第N阶段:。。。。
总之,后来NodeW现在进化到现在这个样子了:(以后可能还会进化)
var NodeW = function(core) {
if (!core) {//用法:var w=NodeW(null); 返回null
return null;
}
var arg1 = arguments[1];
if (isString(core)) {
if (/^</.test(core)) { //用法:var w=NodeW(html);
var list = create(core, true, arg1).childNodes,
els
= [];
for (var i = 0, elI; elI = list[i]; i++) {
els[i]
= elI;
}
return new NodeW(els);
}
else { //用法:var w=NodeW(sSelector);
return new NodeW(query(arg1, core));
}
}
else {
core
= g(core, arg1);
if (this instanceof NodeW) {
this.core = core;
if (isArray(core)) { //用法:var w=NodeW(elementsArray);
this.length = 0;
push.apply(
this, core);
}
else { //用法:var w=new NodeW(element)//不推荐;
this.length = 1;
this[0] = core;
}
}
else {//用法:var w=NodeW(element); var w2=NodeW(elementsArray);
return new NodeW(core);
}
}
};
更多代码参见:http://dev.qwrap.com/resource/js/dom/node.w.js
不过,这是他没配装备的样子。以后讲到retouch时,会再次细述如何用NodeH、EventTargetH、JssTargetH、ArrayH等来武装它,让他更健壮强大。
NodeW的一个静态方法就是为以后的武装作准备的,它的介绍也先按下不提。

附:QWrap地址:http://www.qwrap.com
原文地址:https://www.cnblogs.com/jkisjk/p/qwrap_wrap_NodeW.html