zoukankan      html  css  js  c++  java
  • 《JavaScript编程精解》读书笔记第五章:函数式编程

    5.1抽象:归根结底程序是要解决生活中的问题,但多数时候现实中的问题总是很复杂,而尽量降低程序复杂程度的办法就是进行抽象化处理。把许多实际的复杂关系抽象成更简单的逻辑运用到程序当中。这是我对编程中抽象的理解。函数式编程就是通过巧妙的函数组合来创建抽象。

    5.2高阶函数:简单点说高阶函数就是处理其他函数的函数,也就是函数的嵌套。js是面向函数的一门语言,在js的世界里任何东西都是值类型的,当然函数也不例外。它与其他语言(比如说C#)最明显的差异就是函数能够完全像值一样去生成,去传递。你可以把函数作为另一个函数的参数来使用,也可以在一个函数的内部再定义一个新的函数。当然你也许会想到C#中的lambda表达式,确实也能完成一些类似的功能,但js是纯动态的语言,处理函数是它的看家本领,C#中lambda,var的运用只是在一定程度上增加了动态性,灵活性上还是远不如js的。前面说的这些是我个人的一些理解,书中并没讲这些,书里面多是以实例的形式来展现函数编程的,下面我们用代码试试看(当然,代码也一定跟书上一样,有些是高仿的)。

    一个最简单的函数式编程的例子

    View Code
    function calculate(calMethod,numA,numB)
    {
        return calMethod(numA,numB);
    }
    function add(num1,num2)
    {
        return num1+num2;
    }
    function multiply(num1,num2)
    {
        return num1*num2;
    }
    var result1 = calculate(add,1,2);
    var result2 = calculate(multiply,1,2);
    alert("result1:"+result1+",result2:"+result2);

     在上面的例子中,函数就是值的特性一目了然。还有一个常用的高阶函数的类型就是修改函数,作用是修改了传入的函数的值,如下: 

    View Code
    function initMethod(method1)
    {
        return function(num1)
        {
            return method1(num1);
        }
    }
    
    function changedMethod(num1)
    {
        return num1*num1;
    }
    
    var testMethod = initMethod(changedMethod);
    var result = testMethod(2);
    alert(result);

    上面这个例子让我想起最近asp.net的mvc中的依赖注入,把带有具体功能的一个对象通过C#中构造函数传到当前类中,当前类中的功能其实是靠传过来的对象实现的。代理模式应该也是这样的。

    我们常见的sum()函数,其实是一个算法的变体,而这个函数就是规约函数,下面是规约函数的一个例子:

    View Code
    //规约函数
    function reduce(combine,base,array){
        forEach(array,function(element){
            base = combine(base,element);
        });
        return base;
    }
    
    function add(a,b){
        return a+b;
    }
    
    function forEach(array,action){
        for(var i = 0; i< array.length; i++){
            action(array[i]);
        }
    }
    function sum(numbers){
        return reduce(add,0,numbers);
    }
    alert(sum([1,2,3]));

     reduce函数通过重复的调用一个函数,将一个数组里面的所有的值都加到一个基础数据base上。这样就能对数组里的所有值按照一定规则计算,这里是相加,改变combine参数来具体实现你自己想要的运算。另外注意的是函数作为参数传递时放在第一位置,这是惯例,至于具体原因,书中声明后面会讲到。

    到此为止我稍微总结了一下函数内部读取函数的情况:

    1.条用外部的函数(最起码的)。

    2.能够读取作为参数传进来的函数。

    3.能够在当前函数内部定义函数,并调用该函数。

    4.能够读取该函数自身(递归)。

    再看一个函数,目的是接受一个数组,返回数组里面值为0的个数。

    View Code
    function reduce(combine,base,array){
        forEach(array,function(element){
            base = combine(base,element);
        });
        return base;
    }
    function forEach(array,action){
        for(var i = 0; i< array.length; i++){
            action(array[i]);
        }
    }
    function countZeros(array){
        function counter(total,element){
            return total + (0 === element ? 1 : 0);
            //下面注释掉的这一行是一个替代方案
            //return total + (0 === element);
        }
        return reduce(counter,0,array);
    }
    alert(countZeros([0,1,0,0,0,0,1,1,1,1]));
    
    
    /*帅!,能把true转成整型啊,true就是1,false就是0.
    var a = 1 + true;
    alert(a);
    */
    
    /*
    var a = 1+ false;
    alert(a);
    */

       上面这个例子中return total + (0 === element ? 1 : 0)用的十分巧妙,但我尝试了一种新的实现方法return total + (0===element),经过实验我发现其实bool与number相加的时候true会变成1,false会变成0,这样在很多情况下都可以判断跟加减运算一气呵成了,挺好。

    接下来是一个映射数组,这是一个与数组相关的基本算法。跟前面的规约函数一样可以处理数组中的每个数值,但是函数的返回值并不会被丢弃,而是重新构建一个函数。

    View Code
    function forEach(array,action){
        for(var i = 0; i< array.length; i++){
            action(array[i]);
        }
    }
    
    //映射数组
    function map(func,array){
        var result = [];
        forEach(array,function(element){
            result.push(func(element));
        });
        return result;
    }
    alert(map(Math.round,[1.1,3.3,2.2]));

     5.4其他函数技巧:在用高阶函数的时候,js操作符都不是函数,就像前面的例子,我们需要定义一个add函数,但每次这样编写并调用显然很烦躁,我们可以这样干:

    View Code
    var op = {
        "+":function(a,b){return a+b;},
        "==":function(a,b){return a==b;},
        "===":function(a,b){return a===b;},
        "!":function(a){return !a;}       
        /*等等,可以任意添加自己常用的操作*/
    }
    
    //下面的方式来完成求和
    reduce(op["+"],0,[1,2,3,4,5,6]);

     备注:javascript常用函数

    1.call()  via

    View Code
    //call()函数的用法一
    function Class1() 
    { 
        this.name = "class1"; 
    
        this.showNam = function() 
        { 
            alert(this.name); 
        } 
    } 
    
    function Class2() 
    { 
        this.name = "class2"; 
    } 
    
    var c1 = new Class1(); 
    var c2 = new Class2(); 
    //call函数使得c1中的方法能够在c2这个对象上执行
    c1.showNam.call(c2); //result:class2
    
    
    //call()函数的用法二
    function Class1() 
    { 
        this.showTxt = function(txt) 
        { 
            alert(txt); 
        } 
    } 
    function Class2() 
    { 
        //在Class2中调用Class1.call,就是把Class1中的对象覆盖当前对象,以此来完成继承。
        Class1.call(this); 
    } 
    var c2 = new Class2(); 
    c2.showTxt("cc"); 

     2.与call()相对应的还有一个apply()方法。关于两者的差异请看:http://www.cnblogs.com/fighting_cp/archive/2010/09/20/1831844.html

    后记:这一章的内容虽然总量不大,但牵扯到算法的比较多,还有一部分自己没理解好的就没写。自己不理解的,真是写不出来,以后开始每天看点,慢慢的补充上。 

  • 相关阅读:
    人生转折点:弃文从理
    人生第一站:大三暑假实习僧
    监听器启动顺序和java常见注解
    java常识和好玩的注释
    182. Duplicate Emails (Easy)
    181. Employees Earning More Than Their Managers (Easy)
    180. Consecutive Numbers (Medium)
    178. Rank Scores (Medium)
    177. Nth Highest Salary (Medium)
    176. Second Highest Salary(Easy)
  • 原文地址:https://www.cnblogs.com/zhangran/p/2788370.html
Copyright © 2011-2022 走看看