JavaScript Cookbook 2nd 之 Function

Stormy17L 8年前發布 | 6K 次閱讀 JavaScript開發 JavaScript

昨晚翻了一下,雖然都是一些舊知識,不過深入下去對照著其他資料一起看,還是能發現一些有意思的地方。

函數式編程

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

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

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

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);
    }
  }
}

而函數式編程,我們則可以這樣寫業務邏輯。

// 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() 可以用在有大運算量的業務場景。

 

來自:https://www.mxgw.info/t/javascript-cookbook-2nd-function.html

 

 本文由用戶 Stormy17L 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!