瞭解 JavaScript 函數語言程式設計 - 宣告式函式

Pandaaa發表於2019-05-05

瞭解JavaScript函數語言程式設計目錄

宣告式

宣告式程式碼

什麼是宣告式,我們將不再指示計算機如何工作,而是指我們明確希望得到的結果。這種程式設計方式會改變我們習以為常的指令式程式設計相比,會讓我們的輕鬆許多。

和命令式不同,宣告式意味著我們要寫表示式,而不是一步一步的指示。

理解宣告式程式碼

// 命令式
var makes = [];
for (i = 0; i < cars.length; i++) {
  makes.push(cars[i].make);
}


// 宣告式
var makes = cars.map(function(car){ return car.make; });
複製程式碼

命令式的迴圈要求你必須先例項化一個陣列,而且執行完這個例項化語句之後,直譯器才繼續執行後面的程式碼。然後再直接迭代 cars 列表,手動增加計數器,把各種零零散散的東西都展示出來...實在是直白得有些露骨。

使用 map 的版本是一個表示式,它對執行順序沒有要求。而且,map 函式如何進行迭代,返回的陣列如何收集,都有很大的自由度。它指明的是做什麼,不是怎麼做。因此,它是正兒八經的宣告式程式碼。

  • 有一個普遍的說法,“雖然如此,但是使用命令式迴圈的速度要快很多”,這裡推薦一個視訊大家可以看看 JIT 優化程式碼 (需要梯子)

再看一個栗子

// 命令式
var authenticate = function(form) {
  var user = toUser(form);
  return logIn(user);
};

// 宣告式
var authenticate = compose(logIn, toUser);
複製程式碼

看過前面文章的朋友應該明白 compose 組合程式碼的意義。

雖然命令式的版本並不一定就是錯的,但還是硬編碼了那種一步接一步的執行方式。而 compose 表示式只是簡單地指出了這樣一個事實:使用者驗證是 toUser 和 logIn 兩個行為的組合。這再次說明,宣告式為潛在的程式碼更新提供了支援,使得我們的應用程式碼成為了一種高階規範(high level specification)。

宣告式最重要的是不是指定執行順序,所以它天然的適合進行並行運算。它和純函式一起解釋了為何函數語言程式設計是未來平行計算的一個不錯的選擇 -- 我們真的不需要做什麼就能現實一個並行/併發系統。

副作用

如果函式或表示式修改程式的某些狀態(除了返回值之外)在其自身範圍之外或具有與其呼叫函式或外部的可觀察變數,則稱其具有副作用。

  • 上面這句話有點難以理解,我們來看一個栗子
let meetup = {name:'JS',isActive:true,members:49};
const scheduleMeetup = (date, place) => {
  meetup.date = date;
  meetup.place = place;
  if (meetup.members < 50)
    meetup.isActive = false;
}
const publishMeetup = () => {
  if (meetup.isActive) {
    meetup.publish = true;
  }
}
scheduleMeetup('today','Bnagalore');
publishMeetup();
console.log(meetup);
複製程式碼

上面的這個程式有副作用,因為函式scheduleMeetup的實際職責是新增 dateplace,但它正在修改 isActive 的值以及其他一些函式 publishMeetup 所依賴的值,以及 publishMeetup 函式將作為副作用沒有所需的輸出,因為它的輸入在兩者之間發生了變化。在程式)中,除錯副作用會變得非常困難。

  • 所以我們需要純函式和宣告式函式來隔離這種無用的錯誤。

為什麼使用宣告式方式的函式式函式?

  • 在純函式中,我們總是能保證我們的輸出。
  • 低複雜度,我們只需要考慮的是他是做什麼的,而不是在乎過程怎麼完成的。
  • 易於測試,我們不用依賴於函式的狀態,我們只關心結果的驗證。
  • 函數語言程式設計更加易於理解。

總結

宣告式和命令式的區別和含義,這裡我們可以結合上一篇文章 組合程式碼 相關知識。

相關文章