JavaScript函数式编程学习

 

原文地址 http://jiarry.bokee.com/6768082.html 

 

对函数式编程了解较少,这些天看了一些文章,逐渐开始理解。其实以前在用的带有这种风格,只是并没有这么彻底,或者说“o,这就是函数式编程”,比如尽量限制变量的作用域到一个比较小的范围,我们再整个类里面不用到什么变量,全是通过函数来相互调用的。

当然对于这种模式或者概念而言,其实是没有特别的定论的。不过函数式编程的确由来已久,而最近两年才开始慢慢地热了起来。可能,原因是这种适合函数式编程的动态语言开始流行了。看一下函数式编程的介绍,如下:

什么是函数编程?
 
在经常被引用的论文 “Why Functional Programming Matters”(请参阅 参考资料) 中,作者 John Hughes 说明了模块化是成功编程的关键,而函数编程可以极大地改进模块化。在函数编程中,编程人员有一个天然框架用来开发更小的、更简单的和更一般化的模块,然后 将它们组合在一起。函数编程的一些基本特点包括:
支持闭包和高阶函数。
支持懒惰计算(lazy evaluation)。
使用递归作为控制流程的机制。
加强了引用透明性

  • 函数式编程概念,包括匿名函数、调用函数的不同方法,以及将函数作为参数传递给其他函数的方式。函数式编程是一种强调表达式的计算而非命令的执行的一种编程风格。表达式是用函数结合基本值构成的,它类似于用参数调用函数。
  • 函数式概念的运用,采用的示例包括:扩展数组排序;动态 HTML 生成的优美代码;系列函数的应用。

这些,JavaScript刚好符合。而JavaScript程序由于没有统一的包定义或者装载模式,脚本量大了时不太好维护,不够模块化。有时一个函数 块包括上百行代码,这对于代码的维护和可读性会是个问题。虽然,面向对象能有效解决了这一点,但是无形间文件似乎多了,代码似乎也增加了。

JavaScript为什么要使用函数式编程的方式呢?既然JavaScipt通过prototype可以完全实现面向对象,那我们为什么要函数式编程 呢?我想有很重要的一点就是,无论怎么说面向对象的设计把一些东西确实搞复杂了,更臃肿庞大了。这对于大型应用来讲是无可厚非的,但是通常 JavaScript我们用来实现的Web应用并不是企业级的,我们需要简单、高效,同时又维护性强,可复用的代码。

函数式JavaScript编程之所以很重要有三条主要的理由:
   1. 它有助于写出模块化和可复用的代码。不用再看到冗长的函数块了。
   2. 它对事件(event)处理程序非常有效。匿名函数发挥了重要作用。
   3. 它很有趣!。或者说很Cool。

今天抽空利用一些函数式编程写了一个程序,作用是拼接HTML字符串,同时给HTML Element加上函数事件。代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<html>
<head>
<title> new document </title>
<meta name="generator" content="editplus">
<meta name="author" content="">
<meta name="keywords" content="">
<meta name="description" content="">
<style>
.link{background:#f4ffff;}
.span{background:#f4f4f9;}
.strong{background:#fefaf5;}
</style>
</head>

<body>
    函数调用测试,分别给对象绑定事件和函数
    <div id="div1"></div><br/>
    <div id="div2"></div><br/>
    <div id="div3"></div><br/>
    
<script language="javascript">
<!--
/**
 * @file htmlTemplateTest.js
 * @description 
 * 函数式编程测试,拼接组合HTML字符串并输出,然后分别给HTMLtag编定相关事件
 * 先定义一个数组和组合函数,然后通过数组的实例来调用函数返回字符串
 * @author jarry
 * @date 2008-07-25
 */

/*
 * @description 返回HTML Element对象
 * @param <string> id 对象的id
 * @return 返回HTML对象
 */
function G(id) {
        return document.getElementById(id) || document.all[id];
}

// 测试数组
var StringArrTest = {
     htmlString : ["<a href=/"#/">link</a>", "<span>span</span>", "<strong>strong</strong>"],
     className : ["link", "span", "strong"],
     events : ["mouseover", "mouseout", "click", "dblclick"],
     color : ["fefaf5", "f4f4f9", "F7BC44", "65D6A0"]
}

// 事件与函数对应的对象
var EventsBindFunction = {
    "mouseover" : "onDivOver",
    "mouseout"  : "onDivOut",
    "click"     : "onDivClick",
    "dblclick"  : "onDivDblClick",    
    "color" : StringArrTest.color,
        
    onDivOver : function() {
            window.status = (this.innerHTML + " : onDivOver" );
            this.style.background = "#" + EventsBindFunction["color"][0];
    },
    onDivOut : function() {
            window.status = (this.innerHTML + " : onDivOut");
            this.style.background = "#" + EventsBindFunction["color"][1];
    },
    onDivClick : function() {
            window.status = (this.innerHTML + " : onDivClick");
            this.style.background = "#" + EventsBindFunction["color"][2];
    },
    onDivDblClick : function() {
        window.status = (this.innerHTML + " : onDivDblClick");
        alert(this.innerHTML);
        this.style.background = "#" + EventsBindFunction["color"][3];
    }    
}


var functionAction = function() {
        this.color = StringArrTest.color;        
}


/*
 * @description HTML字符串实例,返回HTML字符串
 * @param <string> elemStr 可以是HTML元素字串
 * @param <int> index 当前序号
 * @return <string> HTML字符串
 */
function templateInstance(elemStr, index) {
  return "<div class=/"" + getClassName(StringArrTest.className, index) + "/">" + elemStr + " " + index + " </div>"  ;
}

/*
 * @description 设置class
 * @param <array> arr 可以是HTML元素字串
 * @param <int> index 序号
 * @return <string> 返回class名称
 */
function getClassName(arr, index) {
    return arr[index];
}

/**
 * @description 给指定对象下的子集加装指定事件
 * @param <element> obj HTML element 对象
 * @param <array> eve 需要绑载的事件数组 
 */
function addEventsForArray(obj, eve) {
    for (var j = 0; j <eve.length; j++ ) {
        
                // 用匿名函数执行作用域
                ( function() {
                        var eves = eve[j];
                        
                        obj["on" + eves] = function() {
                            // 绑定相应函数
                              document.title = eves;
                         }
                         
                     }                     
                 )();
    }
}

/**
 * @description 给指定对象下的子集加装指定事件,且给该事件指定具体行为
 * @param <element> obj HTML element 对象
 * @param <array> arr 需要绑载的事件数组 
 * @param <object> eventFnObject 事件函数对象
 */
function addFnForArray(obj, arr, eventFnObject) {    
    var childDiv = obj.getElementsByTagName("div");
    var eve = arr ;
    var fn = new functionAction();
    //alert(ov.toString());
    for(var i = 0; i < childDiv.length; i++) {
    //循环所有的子对象
            for(var j = 0; j < eve.length; j++) {
    // 循环所有的事件
    
    //匿名函数给每个对象上加装事件
                (                
                    function() {
                    var child   = childDiv[i];
                    var onEvent = eve[j];
                    
                    switch(onEvent) {
                        case onEvent : 
                         child["on" + onEvent] = eventFnObject[ eventFnObject[onEvent] ];
                         // 给该对象的时间挂载指定的函数,eventFnObject里有同名对应的函数
                         // div["onclick"] = eventFnObject["onDivClick"];
                         
                        default      : null;
                    }
                        
                    }
                )();             
         }
     }
}

/**
 * @description 例子1,扩展Array原型fold方法
 * @param <function> templateFn 一个函数对象
 * @return <string> str 返回HTML字符串
 */
Array.prototype.fold = function(templateFn) {
  var len = this.length;
  var str = "";

  for(var i = 0 ; i < len ; i++) {
        str += ( templateFn(this[i], i) );
        // templateFn是传递过来的函数,这个函数数里面可以来组合HTML
    }

  return str;
}

G("div1").innerHTML = (StringArrTest.htmlString.fold(templateInstance));
addFnForArray(G("div1"), StringArrTest.events, EventsBindFunction);
addEventsForArray(G("div1"), StringArrTest.events);

/**
 * @description 例子2,把字符串模板函数作为参数来调用,生成HTML字符串
 * @param <array> arr 数组,组合HTML用
 * @param <function> templateFn 组合HTML用的函数
 * @return <string> str 返回HTML字符串
 */
function genderHTML(arr, templateFn) {
    var len = arr.length;
    var str = "";
    
    for(var i = 0 ; i < len ; i++) {        
        str += ( templateFn(arr[i], i) );
        // 调用生成HTML字符串的函数
        }
    
    return str;
}

G("div2").innerHTML = ( genderHTML(StringArrTest.htmlString, templateInstance) );
addFnForArray(G("div2"), StringArrTest.events, EventsBindFunction);
addEventsForArray(G("div2"), StringArrTest.events);


/**
 * @description 例子3。常见的普通例子,直接返回HTML字符串
 * @param <array> arr 数组,组合HTML用
 * @return <string> str 返回HTML字符串
 */
function getHTML(arr) {
    var len = arr.length;
    var str = "";

    for (var i = 0; i < len; i++) {
      var elemStr = arr[i];
      str += ("<div class=/"" + StringArrTest.className[i] + "/">" + elemStr + " " + i + " </div>" ); 
      // 拼接字符串,会比较长,不采用外调函数的方式
    }
    
    var eve = StringArrTest.events;
    
    for (var j = 0; j <eve.length; j++ ) {
        
                // 用匿名函数执行作用域,跟addEventsForArray一致
                ( function() {
                        var eves = eve[j];
                        
                        G("div3")["on" + eves] = function() {
                            // 绑定相应函数
                              document.title = eves;
                         }
                         
                     }                     
                 )();
    }

    return str;
}


G("div3").innerHTML = (getHTML(StringArrTest.htmlString));
addFnForArray(G("div3"), StringArrTest.events, EventsBindFunction);

//-->
</script>
代码如下:<br/>
<textarea id="code" cols="120" rows="20" style="font-size:12px;background:#ffe">
</textarea>
<script>
(function() {
    var scriptObj = document.getElementsByTagName("script")[0];
    G("code").value = (scriptObj.innerHTML);
}
)();
</script>
参考
http://www.ibm.com/developerworks/cn/web/wa-javascript.html 
http://shiningray.cn/functional_javascript_programming.html 
http://www.javaeye.com/topic/101055?page=1 
http://www.infoq.com/cn/news/2008/03/revoerability-and-testing-oo-fp 
http://www.cnblogs.com/RChen/archive/2007/07/24/829755.html 
</body>
</html>

本次试验主要在函数调用函数以及采用匿名函数挂载事件上,感觉确实有他的好处。采用面向对象实现可能会更复杂,而一般的函数编程会把一个函数搞得很复杂,读起来会比较累。总体感觉是,确实不错的样子。
当然了,在实际开发过程中哦,我还是推崇面向对象的。无论Java、PHP、JavaScript等,面向对象确实让程序变得更简单、更具有复用和可维护 性。但是,毋庸置疑,函数式编程对于OOP将是一个很好的补充。在OOP的大框架里,有时采用一下函数式风格,确实能解决一些问题。 就好象对于事件的处理,如果采用匿名函数来配合,那一切要变得方便的多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值