深刻理解javascript系列(十四):純函數

雖然我是計算機方向畢業的學生,可是認識到純函數,仍是在學react的時候...javascript

相同的輸入總會獲得相同的輸出,而且不會產生反作用的函數,就是純函數。前端

咱們能夠經過一個是否會改變原始數據的兩個一樣的功能的方法來區別純函數與非純函數之間的不一樣。java

但願有能有這麼一個函數,可以獲取到引入數組的最後一項。那麼能夠經過如下兩種方式來實現。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();    //非純函數複製代碼

與這種會改變原始數據的函數相比,純函數明顯更加可靠。很顯然咱們都不但願本身的數據在通過幾回調用後就變得一團糟。緩存

純函數還有一個重要的特色,那就是除了傳入的參數外,不依賴任何外界的信息與狀態。以下面這個例子。bash

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.  純函數的可移植性

在封裝一個函數、一個庫或一個組件時,其實都指望一次封裝,多處使用,而純函數恰好具有這樣的特性。閉包

純函數不依賴參數以外的值,所以純函數的依賴很是明確。也正是如此,咱們纔可以把一些經常使用的功能封裝成一個公共方法,這樣之後遇到相似的場景時就不用再從新封裝了。app

咱們知道一個頁面的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

相關文章
相關標籤/搜索