JavaScript函數(shù)式編程學(xué)習(xí)筆記

函數(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))
}
image.png
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)如下:


image.png
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ù)組拉平方法

https://es6.ruanyifeng.com/?search=%E4%B8%80%E7%BB%B4&x=0&y=0#docs/array#%E6%95%B0%E7%BB%84%E5%AE%9E%E4%BE%8B%E7%9A%84-flat%EF%BC%8CflatMap

總結(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)超過其他編程方式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市钥顽,隨后出現(xiàn)的幾起案子义屏,更是在濱河造成了極大的恐慌,老刑警劉巖蜂大,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闽铐,死亡現(xiàn)場離奇詭異,居然都是意外死亡奶浦,警方通過查閱死者的電腦和手機(jī)兄墅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來澳叉,“玉大人隙咸,你說我怎么就攤上這事〕上矗” “怎么了五督?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓶殃。 經(jīng)常有香客問我充包,道長,這世上最難降的妖魔是什么遥椿? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任误证,我火速辦了婚禮,結(jié)果婚禮上修壕,老公的妹妹穿的比我還像新娘愈捅。我一直安慰自己,他們只是感情好慈鸠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布蓝谨。 她就那樣靜靜地躺著,像睡著了一般青团。 火紅的嫁衣襯著肌膚如雪譬巫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天督笆,我揣著相機(jī)與錄音芦昔,去河邊找鬼。 笑死娃肿,一個(gè)胖子當(dāng)著我的面吹牛咕缎,可吹牛的內(nèi)容都是我干的珠十。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凭豪,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼焙蹭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嫂伞,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤孔厉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后帖努,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撰豺,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年拼余,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了污桦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姿搜,死狀恐怖寡润,靈堂內(nèi)的尸體忽然破棺而出捆憎,到底是詐尸還是另有隱情舅柜,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布躲惰,位于F島的核電站致份,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏础拨。R本人自食惡果不足惜氮块,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诡宗。 院中可真熱鬧滔蝉,春花似錦、人聲如沸塔沃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛀柴。三九已至螃概,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸽疾,已是汗流浹背吊洼。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留制肮,地道東北人冒窍。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓递沪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親超燃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子区拳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容