简单的 JavaScript?
Web 语言 JavaScript 的原意被很多人曲解了,绝大部分的人(包括以前我在做项目的时候)其实都没有正确地使用 HTML 和 CSS。几乎没有人是通过FP的特性规范来学习 JavaScript 的,他们都是通过流行的 OO 设计教材和阅读以前遗留的代码来学习的。很遗憾的是 OO 这两种学习途径对于他们真正理解 JavaScript 理念都没有多少帮助,甚至还存在着大量的误导。比如在编码中仍未摆脱大量的过程式编程,或着更“高级一点”,大量使用伪类来做架构。他们不知道这些东西自从应用了函数式 JS 后全部都是过时了的方法。这是不是 JavaScript 本身的罪过?我并不这样认为。越是简单的东西越容易被误用(当然简单并不意味它不好,不能帮助你完成工作)。有多少 Web 设计教材宣称你只需要三天就可以精通 JavaScript,真实情况当然并不是这样。
高阶函数(Higher Order Function)作为函数式编程众多风格中的一项显著特征,经常被使用着。按照维基百科上面的定义,高阶函数是至少满足下列一个条件的函数:
- 接受函数作为输入
- 输出一个函数
咱们比较难真正地理解这两句话?那不妨详细地解释一下。在函数式语言中,函数不但是一种特殊的对象,而且还是第一等的“公民”,可以把函数作为一种“类型”,因此函数本身是一个可以传来传去的“值”。也就是说,某个一个函数刚开始执行的时候,总可以送入一个“函数的参数”。参数传入了,它本身就是一个函数。当然,这时候的这个输入的函数是相当于“某一个函数”的“另外一个函数”。好了,当函数执行完毕之后,您又可以返回“另外一个新的函数”,取决于您 return fn(){...} 的是什么。于是最后,上述出现三个不同的函数,分别有不同的角色。要达到这样的话,不把函数是作为一个“值”,是不能够实现的。
我们知道,JavaScript 是一门灵活的语言,而且是一门精巧的函数式语言。下面就通过 JavaScript 来实践一下高阶函数。首先是“函数作为参数”的演示,在浏览器中执行
例子一:这是最简单不过的数组排序语句了。实际上 Array.prototype.sort() 还能够支持一个可选的参数“比较函数”,其形式如 sort(fn),fn是一个函数类型(Function)的值,说明了这里就应用到函数式编程的“高阶函数”。我们看看例子二,这是一个对日期类型排序的 sort()。
例子二:
数组排序的时候就会执行 function (x,y) {return x.date-y.date; } 这个传入的函数。按照例子一没有传入任何排序参数,那样默认就是,当 x 大于 y 的时候返回 1,当 x 等于 y 的时候返回 0,当 x 小于 y 的时候返回 -1。如果按照例子二的情形,就是对 x.date 和 y.date 操作而已。日期的“从早到晚”或者数字型的“从小到大”(例子一),无论哪一个,对于 sort() 而言结果都是相同的。另外补充一点,比较日期同样可以使用“加法 +/减法-”的比较早晚,如果字符的话,则可使用<、>比较。
函数作为参数的用法了解过之后,我们来看看作为返回值时候的函数,如例子三为一段内容加上 HTML tag,请看例子三。
例子三:var B = wrap('B'); 这一语句已经决定了这是一个“加粗体”的特别函数,执行该 B() 函数就会发生 <b>……内容……</b> 的效果。若 wrap('div') 便是产生 <div>……内容……</div>,wrap('li') 便是产生 <li>……内容……</li>……如此类推。 wrap('B') 返回到变量 B 的即是一个函数。若不使用变量,也可以直接如等价的例子那样 wrap('B') 亦是合法的JavaScript 语句,之前最后一个括号()前面的是函数类型的值即可。为什么 stag + x + etag 中 stag/etag 没有输入也会在 wrap() 内部定义?因为 warp 作用域中就有 stag、etag 两个变量。如果从理论上描述这一特性,应该属于闭包(closure)方面的内容了。
下面补充一些方法(2014-1-2)
// -------------------fp js --------------------
Function.prototype.map = function(arr, scope){
var fn = this, fixArgs = $$.fixArgs;
return function(){
for(var i = 0, j = arr.length, result = new Array(j); i < j; i++){
result[i] = fn.apply(scope || this, fixArgs(arguments).concat(arr[i]));
}
return result;
}
}
/**
* fn与参数的关系。
*/
Function.prototype.reduce = function(init, arr){
for(var i = 0, j = arr.length; i < j; i++){
init = this.apply(init, arr[i]);
}
return init;
}
/**
* 两个函数都送入相同的参数。
* 注意比较下面的代码
function(){
var json = arguments[1];
if(qy.isQY_Fields){
write.apply(this, [ arguments[0], qy(json), arguments[2] ]);
}else{
write.apply(this, arguments);
}
}
*/
Function.prototype.split = function(_okFn, _failFn){
var fn = this;
function cb(){
if(fn.apply(this, arguments) === true){
return _okFn.apply(this, arguments);
}else if(_failFn && typeof (_failFn) == "function"){
return _failFn.apply(this, arguments);
}
}
return cb;
};
// 修改function的参数
Function.prototype.P = Function.prototype.prefilterAt = function(argIndex, filterFn, isA){
var selfFn = this;
return function(){
var args = Array.prototype.slice.call(arguments, 0);
args[argIndex] = filterFn.apply(this, isA? arguments : [ arguments[argIndex] ]);
return selfFn.apply(this, args);
};
}
Function.prototype.until = function(loopFn, scope) {
var selfFn = this;
// selfFn是判断条件
return function(){
var result;
while(!selfFn.apply(scope || this, arguments)){
result = loopFn.apply(scope || this, arguments);
}
return result;
};
}