《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

后記:這一章的內容雖然總量不大,但牽扯到算法的比較多,還有一部分自己沒理解好的就沒寫。自己不理解的,真是寫不出來,以后開始每天看點,慢慢的補充上。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM