函數(shù)式編程
1. 什么是函數(shù)式編程
函數(shù)式編程(英語:functional programming)或稱函數(shù)程序設(shè)計(jì)陨倡、泛函編程芥牌,是一種編程范式祭陷,它將計(jì)算機(jī)運(yùn)算視為函數(shù)運(yùn)算,并且避免使用程序狀態(tài)以及易變對(duì)象徒扶。即對(duì)過程進(jìn)行抽象粮彤,將數(shù)據(jù)以輸入輸出流的方式封裝進(jìn)過程內(nèi)部,從而也降低系統(tǒng)的耦合度姜骡。
非函數(shù)式編程
myString="my name is String"
var words = [],
count = 0;
text = myString.split(" ");
for (i = 0; count < 4, i < text.length; i++) {
if (!text[i].match(/[0-9]/)) {
words = words.concat(text[i]);
count++;
}
}
console.log(words); // ["my", "name", "is", "String"]
函數(shù)式編程
myString="my name is String"
var words = myString
.split(" ")
.filter(function(x) {
return !x.match(/[1-9]+/);
})
.slice(0, 4);
console.log(words); // ["my", "name", "is", "String"]
2. 純函數(shù)
“函數(shù)”是輸入與輸出之間的關(guān)系导坟,即對(duì)于相同的輸入,永遠(yuǎn)會(huì)得到相同的輸出圈澈,而且沒有任何可觀察的副作用惫周,也不依賴外部環(huán)境的狀態(tài)
var messages = ["Hi", "Hello", "Sup", "Hey", "Hola"];
messages
.map(function(s, i) {
return printSomewhere(s, 100 * i * 10, 100 * i * 10);
})
.forEach(function(element) {
document.body.appendChild(element);
});
3. lambda函數(shù)又稱匿名函數(shù)
如果函數(shù)只需要引用一次,則無需浪費(fèi)函數(shù)名
匿名函數(shù)lambda:lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名康栈,直接對(duì)應(yīng)于其中的lambda抽象递递。是指一類無需定義標(biāo)識(shí)符(函數(shù)名)的函數(shù)或子程序喷橙。所謂匿名函數(shù),通俗地說就是沒有名字的函數(shù)登舞,lambda函數(shù)沒有名字贰逾,是一種簡單的、在同一行中定義函數(shù)的方法
// 在JavaScript中
const lamFun = (x)=> {return x+1}
// 在JAVA中
() -> System.out.println("hello world");
// 在Python中
fun = lambda x, y: x * y
print(fun(2, 3)) // 6
4. abamdan柯里化
所謂"柯里化"菠秒,就是把一個(gè)多參數(shù)的函數(shù)疙剑,轉(zhuǎn)化為單參數(shù)函數(shù)。
// 柯里化之前
function add(x, y) {
return x + y;
}
add(1, 2) // 3
// 柯里化之后
function addX(y) {
return function (x) {
return x + y;
};
}
addX(2)(1)
5. 函數(shù)合成(compose)
如果一個(gè)值要經(jīng)過多個(gè)函數(shù)稽煤,才能變成另外一個(gè)值,就可以把所有中間步驟合并成一個(gè)函數(shù)囚戚,這叫做"函數(shù)的合成"(compose)酵熙。
合成也是函數(shù)必須是純的一個(gè)原因
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };
var shout = compose(exclaim, toUpperCase);
shout("send in the clowns"); // "SEND IN THE CLOWNS!"
6. 函子、容器Functor
簡單的例子
var Container = function(x) {
this._value = x
}
Container.of = x => new Container(x)
Container.of(1)
Container.prototype.map = function(f){
return Container.of(f(this._value))
}
class Functor {
constructor(val) {
this.val = val;
}
map(f) {
return new Functor(f(this.val));
}
}
Functor是一個(gè)函子驰坊,它的map方法接受函數(shù)f作為參數(shù)匾二,然后返回一個(gè)新的函子,里面包含的值是被f處理過的(f(this.val))
一般約定拳芙,函子的標(biāo)志就是容器具有map方法察藐。該方法將容器里面的每一個(gè)值,映射到另一個(gè)容器舟扎。
尾調(diào)用:指某個(gè)函數(shù)的最后一步是調(diào)用另一個(gè)函數(shù)
7. 自調(diào)用函數(shù)和閉包
閉包指的是有權(quán)訪問父作用域的函數(shù)分飞,即使在父函數(shù)關(guān)閉之后。
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
8. 遞歸
歐幾里得算法睹限,尋找兩個(gè)數(shù)的最大公分母
function gcd(a, b) {
if (b == 0) {
// base case (conquer)
return a;
} else {
// recursive case (divide)
return gcd(b, a % b);
}
}
console.log(gcd(12, 8));
console.log(gcd(100, 20));
9. 高階函數(shù)
函數(shù)可以作為函數(shù)被傳入譬猫,也稱為回調(diào)函數(shù),如函數(shù)合成運(yùn)算羡疗。
可以返回函數(shù)作為輸出染服,如函數(shù)柯里化運(yùn)算。
高階函數(shù)是一個(gè)接收函數(shù)作為參數(shù)或?qū)⒑瘮?shù)作為輸出返回的函數(shù)
add = (x) => (y) => x + y
add(10) //(y) => x + y
add(10)(20) // 30
高階函數(shù)的復(fù)雜實(shí)例
function powersOf(x) {
return function(y) {
// this is an anonymous function!
return Math.pow(x, y);
};
}
powerOfTwo = powersOf(2);
console.log(powerOfTwo(1)); // 2
console.log(powerOfTwo(2));
缺點(diǎn):在堆棧中難以識(shí)別叨恨,不方便調(diào)試
榮譽(yù)函數(shù)
map() filter() reduce() 這三個(gè)函數(shù)為函數(shù)式編程帶來便利
實(shí)現(xiàn)一個(gè)類似Map的高階函數(shù)
function mapForEach(arr,fn){
const newArray = []
for(let i=0;i<arr.length;i++){
newArray.push(fn(arr[i]))
}
return [...newArray]
}
const arrs=['May','Marry','Jacken','Yumy','Sandy']
const arrMap = mapForEach(arrs,(item)=>{ return item.length })
console.log(arrMap) //[3, 5, 6, 4, 5]
10. 學(xué)習(xí)完自己的代碼優(yōu)化實(shí)例
多維數(shù)組遍歷
let arr4 = [{name:'one',children:[{name:'two',children:[{name:'three'}]}]}]
function renderList(arrs){
const list = []
function renderArr(arr=[]){
arr.forEach(item=>{
list.push({name:item.name})
if(item.children && item.children.length){
renderArr(item.children)
}
})
}
renderArr(arrs)
return list
}
console.log('lists',renderList(arr4))
// [0: {name: "one"},1: {name: "two"},2: {name: "three"}]
源數(shù)據(jù)結(jié)構(gòu)如下:
const data = [{
id: 5,
label: "工業(yè)",
children:[
{ id: 10012,
label: "塑料加工",
children:[
{id: 10000129,label: "共混料"},
{id: 10000130,label: "母料(色母等)"},
{id: 10000301,label: "木塑復(fù)合材料"}
]
}
]
}]
未優(yōu)化前
renderTreeList ({ list = [], isOpen = false, rank = 0, parentId = [] }) {
list.forEach(item => {
this.treeList.push({
id: item.id,
label: item.label,
rank,
parentId,
isOpen: false,
show: rank === 0,
checked: this.selectedIds.includes(item.id)
})
if (Array.isArray(item.children) && item.children.length > 0) {
let parents = [...parentId]
parents.push(item.id)
this.renderTreeList({ list: item.children, rank: rank + 1, parentId: parents })
} else {
this.treeList[this.treeList.length - 1].lastRank = true
}
})
this.treeList = [...this.isDefaultOpen({ arr: this.treeList, selectedIds: this.selectedIds, isOpen })]
}
優(yōu)化后
renderList (arrs) {
const arrList = []
const renderArr = function ({ list = [], isOpen = false, rank = 0, parentId = [] }) {
list.forEach(item => {
arrList.push({
id: item.id,
label: item.label,
rank,
parentId,
isOpen: false,
show: rank === 0,
checked: true
})
if (Array.isArray(item.children) && item.children.length > 0) {
let parents = [...parentId]
parents.push(item.id)
renderArr({ list: item.children, rank: rank + 1, parentId: parents })
} else {
arrList[arrList.length - 1].lastRank = true
}
})
}
renderArr({ list: arrs })
return arrList
}
es6數(shù)組拉平方法
總結(jié):
函數(shù)式編程的好處:
1.不容易產(chǎn)生bug柳刮,方便測試和并行處理;
2.可以拋棄this痒钝,避免被this指向弄暈秉颗;
3.打包過程中可以更好的利用 tree shaking 過濾無用代碼;
4.有很多庫可以幫助我們進(jìn)行函數(shù)式開發(fā)送矩,比如經(jīng)典的lodash站宗。
缺點(diǎn):
1.存在性能問題,Map益愈、filter這些需要遍歷多次梢灭,增加時(shí)間開銷
2.資源占用夷家,在 JS 中為了實(shí)現(xiàn)對(duì)象狀態(tài)的不可變,往往會(huì)創(chuàng)建新的對(duì)象敏释,因此库快,它對(duì)垃圾回收所產(chǎn)生的壓力遠(yuǎn)遠(yuǎn)超過其他編程方式