QWrap简介之:FunctionH 针对function的Helper

正如《序》里所说,很多同学是由于对函数变换(泛函)感兴趣,从而来了解QWrap的,例如,从月影的博客三言两语说清QWrap核心模式精髓而来的。
FunctionH的代码,也就那么几十行。不过,如果不理解思路,可能会因为“函数变换”的概念有点难于理解,而放弃对QWrap的围观。
本文粗略的介绍一下FunctionH的思路。
FuncitonH,就是什对function的Helper。
javascript中,直观的感觉,Function有好几种,如:静态函数(Funciton)、类(Class)、方法(method),它们都是Function,他们最大的区别,可能就是this所指。
而this是“一个函数,在被调用时所属的对象”,即:它就像是arguments一样,是一个运行属性,也即:这种分类方式是按函数的预期使用场合来分的。
目前,与函数相关的,QWrap有两个Helper:FunctionH与ClassH,似乎还缺一个MethodH。----是的,的确还缺一个,不过因为还没有考虑成熟,所以暂缺。
ClassH则是针对Class类型的Function的Helper。
FunctionH则是针对静态函数类型的Helper。---(部分方法也因为有标准可循,所以也可以针对method,例如:bind)

对于还没了解闭包的同学,请忽略以下内容。
对于想看代码的同学,请先看功能说明,想想自己会怎么实现,再看代码是怎么实现的。如果直接看代码,可能会有点头痛,因为“自己实现某个功能”通常比“看别人是怎么实现某个功能”要简单得多。

针对function的函数可能会有很多,包括函数变换与应用。
可以先看一下prototype框架里与此相关的一段:
View Code
Object.extend(Function.prototype, (function() {
var slice = Array.prototype.slice;

function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}

function merge(array, args) {
array
= slice.call(array, 0);
return update(array, args);
}

function argumentNames() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(
/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(
/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}

function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() {
var a = merge(args, arguments);
return __method.apply(context, a);
}
}

function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
return function(event) {
var a = update([event || window.event], args);
return __method.apply(context, a);
}
}

function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}

function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout
= timeout * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
}

function defer() {
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}

function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}

function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);
};
}

return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());

可能是历史原因,prototype的这段看起来有点乱,大略作一个粗评:
    argumentNames,//获取argumentNames,如函数是经转换过,或是代码被压缩过,都会导致这个取出来的不符要求。
    bind,//固化变换:可能是历史原因,没有按标准实现
    bindAsEventListener,//
    curry,//科里化变换:如函数是经转换过,都会导致curry化的不确定性
    delay,//延时执行
    defer,//延时执行
    wrap,//让我有点迷糊的变换:将本函数当参数传给wraper
    methodize//方法化变换:将静态函数变成对象方法

而QW.FunctionH,代码如下:
View Code
/**
* @class FunctionH 核心对象Function的扩展
* @singleton
* @namespace QW
* @helper
*/
(
function() {

var FunctionH = {
/**
* 函数包装器 methodize,对函数进行methodize化,使其的第一个参数为this,或this[attr]。
* @method methodize
* @static
* @param {function} func要方法化的函数
* @param {string} attr (Optional) 属性
* @return {function} 已方法化的函数
*/
methodize:
function(func, attr) {
if (attr) {
return function() {
return func.apply(null, [this[attr]].concat([].slice.call(arguments)));
};
}
return function() {
return func.apply(null, [this].concat([].slice.call(arguments)));
};
},
/** 对函数进行集化,使其第一个参数可以是数组
* @method mul
* @static
* @param {function} func
* @param {bite} opt 操作配置项,缺省表示默认,
1 表示getFirst将只操作第一个元素,
2 表示joinLists,如果第一个参数是数组,将操作的结果扁平化返回
* @return {Object} 已集化的函数
*/
mul:
function(func, opt) {
var getFirst = opt == 1,
joinLists
= opt == 2;

if (getFirst) {
return function() {
var list = arguments[0];
if (!(list instanceof Array)) {
return func.apply(this, arguments);
}
if (list.length) {
var args = [].slice.call(arguments, 0);
args[
0] = list[0];
return func.apply(this, args);
}
};
}

return function() {
var list = arguments[0];
if (list instanceof Array) {
var moreArgs = [].slice.call(arguments, 0),
ret
= [],
i
= 0,
len
= list.length,
r;
for (; i < len; i++) {
moreArgs[
0] = list[i];
r
= func.apply(this, moreArgs);
if (joinLists) {
if (r) {
ret
= ret.concat(r);
}
}
else {
ret.push(r);
}
}
return ret;
}
else {
return func.apply(this, arguments);
}
};
},
/**
* 函数包装变换
* @method rwrap
* @static
* @param {func}
* @return {Function}
*/
rwrap:
function(func, wrapper, idx) {
idx
|= 0;
return function() {
var ret = func.apply(this, arguments);
if (idx >= 0) {
ret
= arguments[idx];
}
return wrapper ? new wrapper(ret) : ret;
};
},
/**
* 绑定
* @method bind
* @via https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
* @compatibile ECMA-262, 5th (JavaScript 1.8.5)
* @static
* @param {func} 要绑定的函数
* @obj {object} this_obj
* @param {any} arg1 (Optional) 预先确定的参数
* @param {any} arg2 (Optional) 预先确定的参数
* @return {Function}
*/
bind:
function(func, obj) {
var slice = [].slice,
args
= slice.call(arguments, 2),
nop
= function() {},
bound
= function() {
return func.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
};

nop.prototype
= func.prototype;

bound.prototype
= new nop();

return bound;
},
/**
* 懒惰执行某函数:一直到不得不执行的时候才执行。
* @method lazyApply
* @static
* @param {Function} fun 调用函数
* @param {Object} thisObj 相当于apply方法的thisObj参数
* @param {Array} argArray 相当于apply方法的argArray参数
* @param {int} ims interval毫秒数,即window.setInterval的第二个参数.
* @param {Function} checker 定期运行的判断函数。<br/>
对于不同的返回值,得到不同的结果:<br/>
返回true或1,表示需要立即执行<br/>
返回-1,表示成功偷懒,不用再执行<br/>
返回其它值,表示暂时不执行<br/>
* @return {int} 返回interval的timerId
*/
lazyApply:
function(fun, thisObj, argArray, ims, checker) {
checker
= checker || function() {return true; };
var timer = function() {
var verdict = checker();
if (verdict == 1) {
fun.apply(thisObj, argArray
|| []);
}
if (verdict == 1 || verdict == -1) {
clearInterval(timerId);
}
},
timerId
= setInterval(timer, ims);
return timerId;
}
};


QW.FunctionH
= FunctionH;

}());



粗略分析:
methodize: function(func, attr) :
     方法化 对函数进行methodize化,使其的第一个参数为this,或this[attr]。

mul: function(func, opt)
    对函数进行集化,使其第一个参数可以是数组。如果第一个参数是数组,处理可能有:
      0 getAll 针对每一个都运行,并将结果组成的数组返回
      1 getFirst 只运行第一个运行,并返回结果
      2 joinLists 每一个都运行,并将返回不为空的结果concat后返回。
rwrap: function(func, wrapper, idx)
    包装变换,运行函数,并将指定内容(结果或针对的对象)包装起来返回
bind: function(func, obj)
    绑定(固化)this或arguments,参见(ECMA-262, 5th (JavaScript 1.8.5))https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
lazyApply: function(fun, thisObj, argArray, ims, checker)
    懒惰执行某函数:一直到不得不执行的时候才执行。

与prototype框架的相关方法对比,可以看到:
1。QW.FunctionH的helper特征明显,与prototype直接渲染原型不同。
2。methodize功能相似。不过,QW.FunctionH.methodize加了一个参数“attr”,这会在以后的Wrap模型中起到很重要的作用。
3。bind功能相似,不过qw遵循某标准,这不能怪prototype,因为prototype走在了标准前面。
4。QW.FunctionH.rwrap与prototype框架的wrap是不一样的概念。
5。QW.FunctionH.lazyApply更语义化,并且加了一套checker的逻辑。
6。QW多了一个mul,这个会在NodeH的变换中起到很重要的作用。

其实函数变换远不只bind、methodize、mul、rwrap这几个。
好几个同学看到methodize时就问:“那是不是也应该有个相反的unmethodize,或functionlize”。----是的,理论上可以有,并且也曾出现在qwrap的代码里,后来因为没人用,我们把它拿掉了。
有时候,需要在“理论完整”与“有用精简”之间权衡取舍。
另外,QWrap还在js/core与js/dom的目录下加了一个dev的目录http://dev.qwrap.com/resource/js/core/dev/,那里面是一些备用的功能,可以当作参考。
例如,FunctionH.overload = function(func, func_maps, dispatcher) 可以按参数来进行模式匹配而产生重载的效果。





posted on 2011-03-23 16:37  JKisJK  阅读(919)  评论(1编辑  收藏  举报

导航