深入理解javascript系列(十四):純函式

Panthon發表於2019-01-29

雖然我是計算機方向畢業的學生,但是認識到純函式,還是在學react的時候…

相同的輸入總會得到相同的輸出,並且不會產生副作用的函式,就是純函式。

我們可以通過一個是否會改變原始資料的兩個同樣的功能的方法來區別純函式與非純函式之間的不同。

希望有能有這麼一個函式,能夠獲取到引入陣列的最後一項。那麼可以通過以下兩種方式來實現。

function getLast(arr) {
    return arr[arr.length];
}

function getLast_(arr) {
    return arr.pop();
}

var source = [1,2,3,4];

var last = getLast(source);    //返回結果4,原陣列不變
var last_ = getLast_(source);    //返回結果4,原陣列發生改變複製程式碼

getLast與getLast_雖然都能夠獲得陣列的最後一項值,但是getLast_改變了原陣列。而當原陣列被改變,我們再次呼叫該方法時,得到的結果就會變得不一樣。這種不可預測的封裝方式是非常糟糕的,它會把我們的資料搞的非常混亂。在JavaScript原生支援的資料方法中,也有許多不純的方法,我們在使用時要多加警惕,要清晰地知道原始資料的改變是否會留下隱患。

var source = [1,2,3,4,5];

source.slice(1, 3);        //純函式返回[2,3],source不變
source.splice(1, 3);       //非純函式返回[2,3,4],source被改變

source.pop();        //非純函式
source.push(6);      //非純函式
source.shift();      //非純函式
source.unshift();    //非純函式
source.reverse();    //非純函式複製程式碼

與這種會改變原始資料的函式相比,純函式明顯更加可靠。很顯然我們都不希望自己的資料在經過幾次呼叫後就變得一團糟。

純函式還有一個重要的特點,那就是除了傳入的引數外,不依賴任何外界的資訊與狀態。如下面這個例子。

var name = `pan`;

function sayHello() {
    return `Hello, ` + name;
}
sayHello();    //Hello, pan

//當我們有其它需求時需要改變name的值
name = `zhang`;
sayHello();    //Hello, zhang複製程式碼

同樣的呼叫,但是由於sayHello函式依賴於外界的name變數,因此當外界變數發生變化時,函式的執行結果就變得不一樣。很顯然這並不是我們封裝函式時所希望看到的狀況,因為這樣的變化是不可預測的。因此,對於上面的例子,我們應該把name當作一個引數傳入,這樣就能夠直觀地看到該函式執行時會輸出的結果了。

function sayHello(name) {
    return `Hello, ` + name;
}複製程式碼

1.  純函式的可移植性

在封裝一個函式、一個庫或一個元件時,其實都期望一次封裝,多處使用,而純函式剛好具備這樣的特性。

純函式不依賴引數之外的值,因此純函式的依賴非常明確。也正是如此,我們才能夠把一些常用的功能封裝成一個公共方法,這樣以後遇到類似的場景時就不用再重新封裝了。

我們知道一個頁面的URL裡常常會在‘?’後面帶有引數,例如https://www.baidu.com/s?word=javascript&tn=02003390_7_hao_pg。很多時候我們需要從這段URL中,獲取某些引數對應的值。例如,這個例子中的‘word’的值為javascript。那麼想要封裝這樣一個純函式,應該怎麼做了?

function getParams(url, param) {
    if(!/?/.test(url)) {
        return null;
    }

    var search = url.split(`?`)[1];
    var array = search.split(`&`);

    for(var i = 0; i<array.length; i++) {
        var tmp = array[i].split(`=`);
        if(tmp[0] === param) {
            return decodeURIComponent(tmp[1]);
        }
    }
    
    return null;
}
var url= `https://www.baidu.com/s?word=javascript&tn=02003390_7_hao_pg`;
getParams(url, `word`);    //javascript複製程式碼

雖然getParams並非完全健壯,但是已經足以體現純函式可移植的特點。我們可以在任何需要從url中取得引數對應值的地方呼叫該方法。

2.  純函式的可快取性

在實踐中我們可能會處理大量的資料,例如根據日期,得到當日相關的資料,並處理成前端能夠使用的資料。假設我們封裝了一個process方法來處理每天的資料,而這個處理過程會很複雜。如果不快取處理結果,那麼每次想要得到當天的資料時,就不得不從原始資料在轉換一次。當資料的處理足夠複雜時,那麼很可能不是效能最優的解決方案。而純函式的特點是,相同的輸入總能得到相同的輸出,因此如果將處理過每一臺年的資料快取起來,那麼第二次或者更多次的想要得到的當天的資料時,就不用經歷複雜的處理過程了。

//傳入日期,獲取當天的資料
function process(date) {
    var result = ``;
    //略掉中間複雜的處理過程

    return result;
}

function withProcess(base) {
    var cache = {};
    return function() {
        var date = arguments[0];
        if(cache[date]) {
            return cache[date];
        }
        return base.apply(base, arguments);
    }
}

var _process = withProcess(process);

//經過上面一句程式碼處理之後,就可以使用_process來獲取我們想要的資料了。
//如果資料存在,就返回快取中的資料,如果不存在,則就呼叫process方法重新獲取。複製程式碼

 上面利用了閉包的特性,將處理過的資料都快取在了cache中。這種方式算是高階函式的運用了。

什麼是純函式,純函式有什麼特點,以及為什麼要儘量使用純函式想必這個記錄也算完整了。

雖然在實踐中並不是所有的場景都能夠使用純函式,但還是應儘量在合適的場景使用它。

感謝陽波大神。

這些都是我以往的學習筆記。如果您看到此筆記,希望您能指出我的錯誤。有這麼一個群,裡面的小夥伴互相監督,堅持每天輸出自己的學習心得,不輸出就出局。希望您能加入,我們一起終身學習。歡迎新增我的個人微訊號:Pan1005919589

相關文章