QWrap简介之:FunctionH 针对function的Helper
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框架里与此相关的一段:
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,代码如下:
/**
* @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) 可以按参数来进行模式匹配而产生重载的效果。