天天看點

了解 JavaScript 函數式程式設計

函數式編(Function Programming, FP) 是程式設計範式之一。

程式設計範式即程式設計規範,程式設計風格

我們常聽聽說的程式設計範式還有:

  • 面向對象:典型的就是​

    ​java​

    ​​ 語言,目前​

    ​javascript​

    ​​ 的超集​

    ​typescript​

    ​ 也是走這條路線。也就是把事物抽象成程式世界中的類和對象,通過封裝、繼承和多态來示範事物之間的聯系。
  • 面向過程:典型的就是​

    ​C​

    ​ 語言。在面向過程程式設計中,問題被看成是一些列需要完成的任務,函數則用于完成這些任務,可以看成一步步更新打怪。

而我們今天要讨論的程式設計範式 -- 函數式程式設計把事物和事物直接的聯系抽象到程式世界,強調的是函數的計算,對運算過程進行抽象。使用函數進行程式設計。

純函數

純函數 指相同的輸入永遠得到相同的輸出,而且沒有副作用。

如下: ​

​slice​

​​ 是純函數,​

​splice​

​ 就不是。

let arr = [1, 2, 3];
console.log(arr.slice(0,1)); // 1
console.log(arr.slice(0,1)); // 1
console.log(arr.slice(0,1)); // 1

let another_arr = [3, 4, 5];
console.log(another_arr.splice(0,1)); // 3
console.log(another_arr.splice(0,1)); // 4
console.log(another_arr.splice(0,1)); // 5      

純函數是函數程式設計的重點。函數是函數程式設計的一等公民。

一等公民,指函數跟其他類型具有相同的地位,也就是說 ​

​function​

​​ 可以當作令一個 ​

​function​

​​ 的參數,也可以作為 ​

​function​

​ 的傳回值,也可以作為變量。

比如:

function demo(cb) {
 cb()
}

function add(x) {
 return function(y) {
   return x + y
 }
}

const val = () => 'jimmy';      

聲明式程式設計

函數式程式設計屬于聲明式程式設計範式。

聲明式程式設計範式:會描述一些列的操作,但是并不會暴露他們是如何實作的或者資料流是如何傳遞的。

比如:

let arr = ['hello', 'world', '!']
// 指令式,暴露具體實作
for(let i = 0; i < arr.length; i++) {
  arr[i] = arr[i].toUpperCase()
}

// 聲明式,隐藏細節
arr.map(str => str.toUpperCase())      

引用透明

引用透明(Referential Transparency)指的是一個函數穿傳入相同的參數,不管運算多少次,永遠會得到相同的值,并且不對外部世界産生任何改變(上面我們已經提到過,就是不産生副作用)。

例子可以參考上面純函數的例子。

又比如:

function change_style() {
  let dom = document.getElementById('demo');
  dom.style.color = 'red';
}
// change_style 對外界産生的副作用,更改了 dom 的樣式      

不可變性

不可變性,就是資料一旦建立後就不會再改變的,所有對不可變性的資料操作傳回的是另一個不可變性的資料。這好比操作 ​

​const​

​ 定義變量一樣。

又比如:

let obj = {
  name: 'jimmy'
}
let person = obj;
person.name = 'Jimmy';
console.log(obj.name); // Jimmy
// 此時,對象 obj.name 資料已經發生了改變(可變)

let obj1 = {
  name: 'jimmy'
}
let person1 = { ...obj, name: 'Jimmy'};
console.log(obj1.name); // jimmy
// 此時,對象 obj.name 資料沒發生變化(不可變)      
  • 純函數
  • 函數是一等公民
  • 聲明式程式設計
  • 引用透明
  • 不可變性
function throttle(fn) {
  let canRun = true;
  return function() {
    if(!canRun) {
      return;
    }
    canRun = false;
    setTimeout(() => {
      fn.call(this, arguments);
      canRun = true;
    }, 1000)
  }
}      

繼續閱讀