JavaScript函數式編程與函數柯里化(進階一)

最近在學習JavaScript函數式編程,瀏覽了知乎前端大神的教程,故做簡

單筆記。

/* ------------------------------------------------------------------------------------------------------------ 
    函數的純與不純
*/
//對於相同的輸入,永遠會得到相同的輸出,而且沒有任何可觀察的副作用,也不依賴外部環境的狀態
//eg:
var arr = [1, 2, 3, 4, 5]
//array.slice是純函數,沒有副作用,對於固定的輸入,輸出總是固定的
var xs = arr.slice(0, 3)   //[1,2,3]
console.log(xs)     //[1,2,3]   
var xs2 = arr.slice(0, 3)
console.log(xs2)    //[1,2,3]
/*輸出是一樣的*/

//array.splice是不純的,它有副作用,對於固定的輸入,輸出是不固定的。
arr.splice(0, 3)
console.log(arr)    //[4,5]
arr.splice(0, 3)
console.log(arr)    //[]
/*輸出不固定,會隨時變化*/

// 在函數式編程序,需要的是slice這樣的純函數,而不是splice這種調用後會將數據弄亂的函數
// 爲什麼函數式編程會排斥不純的函數呢? eg:
var min = 18;
//不純
var checkage = age => age > min; 
// -- > var checkage = function(age){return age>min}
console.log(checkage(20))
//純
var checkage = age => age > 18;
console.log(checkage(20))
//在不純的版本中,checkage這個函數的行爲不僅取決於輸入的參數age,還取決於一個外部的變量Min,換句話說,這個函數
//的行爲需要由外部系統環境決定,對於大型系統來說,這種對於外部狀態的依賴是造成系統複雜性大大提高的主要原因。
//可以注意到,純的checkage把關鍵字18硬編在函數內部,拓展性較差,我們可以在後面的柯里化中看到如何優雅的函數式解決這種問題
//純函數可以有效降低系統複雜度,而卻還有可緩存性

/* ------------------------------------------------------------------------------------------------------------ 
    函數的柯里化
*/
/*
    函數的柯里化(curry)的定義很簡單,傳遞給函數一部分參數來調用他,讓他返回一個函數去處理剩下的參數
    比如說對於加法函數var add = (x,y) => x+y 可以進行柯里化: 
*/
//es5
var add = function (x) { 
    return function (y) { 
        return x+y
    }
}
//es6 純正的函數式編程寫法
var add = x => (y => x + y);
var add2 = add(2)
console.log(add2(2))        //4
var add200 = add(200)
console.log(add200(200))    //400
//將checkage函數柯里化
//es5
var checkage = function (min) {
    return function (age) {
        return age > min
     }
}
var checkage = min => (age => age > min);
var checkage18 = checkage(18)
console.log(checkage18(20)) //true
//實際上柯里化是一種預加載函數的方法,通過傳遞較少的參數,得到一個已經記住了這些參數
//的新函數,某種意義上講,這是一種對參數的“緩存”,是一種非常高效的編寫函數的方法:
/*
import { curry } from 'lodash';

//首先柯里化兩個純函數
var match = curry((reg, str) => str.match(reg));
// --> var match = function curry(reg,str){ return str.match(reg) }
var filter = curry((f, arr) => arr.filter(f));

//判斷字符串裏有沒有空格
var haveSpace = match(/\s+/g);

haveSpace("ffffffff");
//=>null

haveSpace("a b");
//=>[" "]

filter(haveSpace, ["abcdefg", "Hello World"]);
//=>["Hello world"]
*/
/* ------------------------------------------------------------------------------------------------------------ 
    函數組合
*/
//學會了使用純函數以及如何把它柯里化以後,我們很容易寫出包菜式代碼:
// h(g(f(s)))
// 這是一種不優雅的函數式代碼,爲了解決函數嵌套問題,我們要用到函數組合
//兩個函數組合:
var compose = function (f, g) {
    return function (x) {
        return f(g(x))
    }
}
//或者
var compose = (f, g) => (x => f(g(x)));
var add1 = x => x + 1;
var mul5 = x => x * 5;
var res = compose(mul5, add1)(2);
console.log(res)    //15
/*
解析:
add1 : x = function (x) {return x+1}
mul5 : x = function (x) {return x*5}
內部雙調用
f(g(x)) == > f(g(2)) == > f(3) == > return 3*5
}
*/
/*定義的compose就像雙面膠,可以把任意兩個函數結合到一起,可以拓展爲三個,甚至N個*/
var first = arr => arr[0];
var reverse = arr => arr.reverse;
var last = compose(first, reverse);
last([1, 2, 3, 4, 5])
console.log(last([1, 2, 3, 4, 5]))      //5
// last = > compose(f(g(x)))
// -->  
/*  內部詳解:
    compose(  
        f => function(arr){return arr[0]}
        g => function(arr){return arr.reverse}
        x => [1,2,3,4,5]
        g(x)=>[5,4,3,2,1]
        f(g(x))=>5
        return f(g(x)) = > return 5
    )
    last => compose(first,reverse) => 5
    var last = function compose(fisrt,reverse){.....}
*/
var str2 = "Hello"
var strc = str2 => str2.concat("???")
console.log(strc(str2))
//仿寫:   查詢傳入的字符串中是否存在H
var one = str => str.concat("!");
var two = str => str.includes('H');
var strLast = compose(two, one)
strLast("Hello World")
console.log(strLast("Hello World")) //true

參考:知乎:Starkwang

https://zhuanlan.zhihu.com/p/21714695

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章