JavaScript 函数式编程理解笔记 (2)

本笔记经过学习
https://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/ 后书写,很大部分都相同,但一字一句包括代码都是自己书写。

JavaScript 函数式编程理解笔记(2)

JavaScript中的函数式编程

函数式编程风格

      在JavaScript中,函数本身做为一种特殊对象,属于顶层对象,不依赖于其他对象存在。

清单1.JavaScript中的求和
    function sum(){
        var res = 0;
        for (var i = 0; i < arguments.length; i++){
            res += parseInt(argument[i]);
        }
        return res;
    }
    print(sum(1,2,3));
    print(sum(1,2,3,4,5,6));

运行此段代码,结果如下:

    6
    31

      如果要完全模拟函数式编码的风格,我们可以定义一些诸如以下的小函数以及谓词:

清单2.一些简单的函数抽象
     function add(a, b){  return a+b; } 
     function sub(a, b){  return a-b; } 
     function mul(a, b){  return a*b; } 
     function div(a, b){  return a/b; } 
     function rem(a, b){  return a%b; } 
     function inc(x){  return x + 1; } 
     function dec(x){  return x - 1; } 
     function equal(a, b){  return a==b; } 
     function great(a, b){  return a>b; } 
     function less(a, b){  return a<b; }
清单3.函数式编程风格
    //修改之前的代码
    function factorial(n){
        if(n == 1){
            return 1;
        }else{
            return factorial(n-1) * n;
        }
    }

    //更接近“函数式”编程风格的代码
    function factorial(n){
        if(equal(n,1)){
            return 1;
        }else{
            return mul(n,factorial(dec(n)));
        }
    }
闭包及其使用

      当在一个 函数outter内部定义一个函数inner,而inner又引用了outter作用域内的变量,在outter之外 使用inner函数,则形成了闭包。

清单4.一个闭包的例子
    function outter(){
        var n = 0;
        return function (){
            return n++;
        }
    }
    var o1 = outter();
    o1();     //n == 0
    o1();     //n == 1
    o1();     //n == 2
    var o2 = outter();
    o2();     //n == 0
    o2();     //n == 1

      匿名函数 function(){return n++;}中包含对outter局部变量n的引用,因此 当outter返回时,会保留n的值(不会被垃圾收集器回收),持续调用o1会改变n的值。而o2的值不会随着o1的调用而改变,是因为o1和o2是不同的实例。

清单5.jQuery中的闭包
    var con = $("div#con");
    setTimeout(function(){
        con.css({background:"gray"});
    },2000);

      上面的代码使用了jQuery的选择器,找到id为con的div元素,注册计时器,两秒后将此元素的背景设置为灰色。神奇之处在于,在调用了setTimeout函数之后,con依旧被保留在了函数中,两秒之后,这个元素的背景色的确改变了。应注意的是,setTimeout在调用之后已经返回了,但con没有被释放,因为con引用了全局作用域里的变量con。

      由于闭包的特殊性,要小心使用闭包,来看一个容易令人困惑的例子:

清单6.错误的使用闭包
    var outter = [];
    function clouseTest (){
        var array = ["one", "two", "three", "four"];
        for (var i = 0; i< array.length; i++){
            var x = {};
            x.no = i;
            x.text = array[i];
            x.invoke = function (){
                print(i);
            }
            outter.push(x);
        }
    }
清单7.错误的结果
    clouseText();     //调用这个函数,向outter数组中添加对象
    for (var i = 0; i < outter.length; i++){
        outter[i].invoke();
    }

出乎意料的是,将打印:

    4
    4
    4
    4

      而不是1,2,3,4这样的序列。每一个x都有自己的no,text,invoke字段,但是invoke却打印了最后一个i的值。错误的原因在于注册的函数:

清单8.错误的原因
    function invoke(){
        print(i);
    }

      每一个invoke都是如此,只有当outter[i].invoke被调用的时候,i的值才会被取到,而i由于是局部变量,for循环退出时i的值总是为4,所以每次打印的结果都会是4。因此,需要对这个函数进行更改:

清单9.正确的用法
    var outter = [];
    function clouseTest2 (){
        var array = {"one", "two", "three", "four"};
        for (var i = 0; i < array.length; i++){
            var x = {};
            x.no = i;
            x.text = array[i];
            x.invoke = function (no){
                return function(){
                    print(no);
                }
            }(i);
            outter.push(x);
        }
    }

通过将函数柯里化,可以达到我们预期的效果,即打印出1,2,3,4这样的序列。

实际应用中的例子
优雅的jQuery

      jQuery实现了完美的CSS选择器,并提供跨浏览器的支持:

清单10.jQuery选择器
    var cons = $("div.note");// 找出所有具有 note 类的 div 
    var con = $("div#con");  // 找出 id 为 con 的 div 元素
    var links = $("a");      // 找出页面上所有的链接元素

      jQuery的选择器规则很丰富。这里需要注意的是,jQuery选择出来的jQuery对象本质上是一个List。
      有了List,我们可以这么操作:

清单11.jQuery操作jQuery对象(List)
     cons.each( function (index){ 
      $( this ).click( function (){ 
        //do something with the node 
      }); 
     });

      同时,我们也可以扩大或缩小这个List:

清单12.扩大/缩小jQuery集合
    cons.find("span.title");// 在 div.note 中进行更细的筛选
    cons.add("div.warn");   // 将 div.note 和 div.warn 合并起来
    cons.slice(0, 5);       // 获取 cons 的一个子集

      现在有一个小例子,假设有这样一个页面:

清单13.页面的HTML结构
    <div class="note"> 
    <span class="title">Hello, world</span> 
    </div> 
    <div class="note"> 
    <span class="title">345</span> 
    </div> 
    <div class="note"> 
    <span class="title">Hello, world</span> 
    </div> 
    <div class="note"> 
    <span class="title">67</span> 
    </div> 
    <div class="note"> 
    <span class="title">483</span> 
    </div>

效果图如下:

图1.过滤之前的效果

      过滤前的效果
      我们通过jQuery对这个集合做一个过滤,在这个例子中,我们保留这样的div,当且仅当这个div中包含一个类名为title的span,而且span中的内容是数字:

清单14.过滤集合
    var cons = $("div.note").hide();   //选择note类的div,并隐藏
    cons.filter(function (){
        return $(this).find("span.title").html().match(/^\d+$/);
    }).show();

效果图如下:

图2.过滤之后的效果

      过滤之后的效果

      再来看看jQuery中对数组的操作:

清单15.jQuery对数组的函数式操作
    var mapped = $.map([1,2,3,4,5,6,7,8,9,10],
    function (n){
        return n + 1;
    });
    var greped = $.grep([1,2,3,4,5,6,7,8,9,10],
    function (n){
        return n % 2 ==0;
    });

      mapped将会变为[2,3,4,5,6,7,8,9,10,11],而greped会变为[2,4,6,8,10]。
      最后看一个更接近实际的例子:

清单16.一个页面刷新的例子
    function update (item){
        return function (text){
            $("div#"+item).html(text);
        }
    }
    function refresh (url, callback){
        var params = {
            type: "echo",
            data: ""
        };
        $.ajax({
            type: "post",
            url: url,
            cache: false,
            async: true,
            dataType: "json",
            data: params,
            success: function (data, status){
                callback(data);
            },
            error: function (err){
                alert("error:" + err);
            }
        });
    }

      首先声明一个柯里化的函数 update,这个函数会将传入的参数作为选择器的 id,并更新这个 div 的内容 (innerHTML)。然后声明一个函数 refresh,refresh 接受两个参数,第一个参数为服务器端的 url,第二个参数为一个回调函数,当服务器端成功返回时,调用该函数。
      这种模式在实际的编程中相当有效,因为关于如何与服务器通信,以及如果选取页面内容的部分被很好的抽象成函数,现在我们需要做的就是将 url 和 id 传递给 refresh,即可完成需要的动作。函数式编程在很大程度上降低了这个过程的复杂性,这正是我们选择使用该思想的最终原因。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值