《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

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

posted @ 2012-11-25 23:41  张小然  阅读(2067)  评论(4编辑  收藏  举报