《JavaScript Cookbook 2nd》之 Function

思惟導圖

昨晚翻了一下,雖然都是一些舊知識,不過深刻下去對照着其餘資料一塊兒看,仍是能發現一些有意思的地方。javascript

函數式編程

反正以前我是沒搞懂函數式和命令式的區別,也很疑惑函數式編程中,若是出現分支怎麼辦,昨晚總算弄明白了。html

// 咱們有4個基礎函數,會根據不一樣的業務邏輯進行組裝使用
// 自動建立
function autoCreate () {}
// 自動同步
function autoSync () {}
// 流程 A
function processA () {}
// 流程 B
function processB () {}
// 流程 A 與流程 B 在業務上是互斥的

傳統的命令式編程,咱們會這樣寫業務邏輯java

function service (errorHandler) {
  
  var result;

  if (!id) {
    result = autoCreate();
    if (result.error) {
      errorHandler(result);
    }
  }

  if (type === 'a') {
    processA();
    if (result.error) {
      errorHandler(result);
    }
  }

  if (type === 'b') {
    result = processB();
    if (result.error) {
      errorHandler(result);
    }
  }

  if (!isSync) {
    result = autoSync();
    if (result.error) {
      errorHandler(result);
    }
  }
}

而函數式編程,咱們則能夠這樣寫業務邏輯。ajax

// service 自己不是一個異步業務,因此直接使用 Promise.resolve()
var service = Promise.resolve();

// 須要對autoCreate()等四個基礎函數作 Promise 改造
service.then(autoCreate)
       .then(processA)
       .then(processB)
       .then(autoSync)
       .catch(errorHandler);

這裏可能會有一個疑惑,互斥的 processA 和 processB 怎麼進入了同一個處理流程,這樣和需求就不符合了?編程

在這種狀況下,咱們還須要在 processA 和 processB 的內部,把退出條件補上。瀏覽器

function processA () {
  return new Promise(function(resolve, reject){

    if (type !== 'A') {
      resolve();
    }

    // 這裏繼續 processA 的邏輯代碼

  });
}

調用棧

JS 在執行的時候,有一個函數調用棧,棧裏面放着一個個的函數調用幀,這些幀保存着所屬函數所需的全部變量信息。異步

函數調用會在內存造成一個「調用記錄」,又稱「調用幀」(call
frame),保存調用位置和內部變量等信息。若是在函數A的內部調用函數B,那麼在A的調用幀上方,還會造成一個B的調用幀。等到B運行結束,將結果返回到A,B的調用幀纔會消失。若是函數B內部還調用函數C,那就還有一個C的調用幀,以此類推。全部的調用幀,就造成一個「調用棧」(call
stack)。函數式編程

瀏覽器攔截 window.open

咱們發現有時候執行 window.open(),能正常打開新窗口或者新的標籤頁,而有時卻又不行,會被瀏覽器攔截。函數

其緣由是瀏覽器會根據當前調用棧,找到最初的caller,若是不是用戶觸發的,則攔截。優化

尾調用優化

因爲函數調用的時候會生成新的調用幀,當遞歸調用的時候,調用棧中的調用幀增加會很是厲害,最終致使內存耗盡而觸發 RangeError。

若是把函數調用放在函數塊的最後一條語句,且不在使用外層函數的變量了,則外層函數所佔用的調用幀已無存在乎義。在進入內層函數的時候,能夠直接用內層函數的調用幀替換掉外層函數的調用幀,從而大大減小內存佔用。

其餘

Partial Application 的模式,用來作 Library 和 SDK 都挺好的。一個可定製性高的底層接口,再經過相似 _.partial() 的方式,提供一個開箱即用的上層接口。就像 $.ajax()$.get() 同樣。

_.defer()_.memoize() 能夠用在有大運算量的業務場景。


同步於個人博客

相關文章
相關標籤/搜索