『翻譯』3個緣由讓我像躲避瘟疫同樣避免使用JS匿名函數

Read the originaljavascript


前言

不管什麼時候閱讀代碼,你必定會看到匿名函數。有時它們被稱爲lambdas,有時被稱爲匿名函數(anonymous functions),但無論怎樣,我認爲他們是糟糕的。java

若是你不知道什麼是匿名函數,這裏有一段引用:git

匿名函數是一個能夠在運行時動態聲明的函數。之因此稱爲匿名函數是由於:他們沒有像普通函數同樣被賦予名字(name)。 ——Helen Emerson, Helephant.comgithub

它們看起來有點像這個:web

function() { ... code ... }

OR

(args) => { ... code ... }複製代碼

下面我會舉一些例子給你,實際上,只有在非用不可的狀況下才會使用匿名函數。它們不是你的首選,而且你要知道爲何。一旦你這麼作了,你的代碼會更簡潔、容易去閱讀,BUG也更容易被捕獲,讓咱們看看這3個避免使用它們的緣由吧!chrome

堆棧跟蹤

你終於寫完了代碼,不管你有多擅長編碼,當你運行代碼時,總會有一些報錯。有些錯誤很容易被捕獲,但有時並非這樣。微信

若是錯誤很容易被捕獲到,好的,那說明你知道他們錯在哪!爲此,咱們使用所謂的堆棧跟蹤,若是你對堆棧跟蹤一點都不瞭解,Google給了咱們很好的介紹ide

假設咱們有一個很簡單的項目:chrome-devtools

function start () {
  (function middle () {
    (function end () {
      console.lg('test');
    })()
  })()
}複製代碼

但看起來咱們作了一些愚蠢得難以置信的事,就像拼錯console.log。在咱們這個小項目中,這沒什麼大不了的。但也許這是一個巨型項目中的一個片斷,有大量的模塊與它相互依賴。最重要的是,讓咱們僞裝你沒有煩這種愚蠢的錯誤。那個初級開發者在他準備離開度假的前一天,把它推到了倉庫中!函數

如今,咱們將去跟蹤錯誤。根據咱們精確的命名函數,咱們獲得的堆棧跟蹤像下面這樣:

幸虧你給函數命名了,初級開發者!如今,咱們能輕鬆的跟蹤到這個BUG。

可是...一旦咱們修復了它,又會出現新的BUG。這時,就要請出更高級的開發者了。他們知道如何使用匿名函數,並大量的使用在他們的代碼中。結果是,他們發現了一個BUG,咱們去跟蹤BUG。

他們的代碼:

(function () {
 (function () {
   (function () {
     console.lg('test');
    })();
  })();
})();複製代碼

多使人驚訝,這個高級開發者也忘了怎麼拼寫console.log!這是一個偶然嗎?!告訴你一個悲痛的消息,他們沒有給函數命名。

控制檯會展現什麼給咱們?

沒事...咱們還有行號提示? 在這個例子中,咱們好像有7行代碼。若是咱們在處理一個巨大的代碼庫?若是每行有10k的代碼?若是行號相差很遠?若是代碼被壓縮了、沒有map文件、行號提示幾乎是沒用的?

我認爲你能夠很是容易的回答這些問題。答案是:你會度過糟糕的一天

可讀性

看吧,我知道你不服氣。你仍然愛着匿名函數,並且你永遠也不會有BUG。抱歉,我忘了告訴你如何寫出完美的代碼。讓咱們來看看這點!

檢查下面兩個不一樣的代碼示例:

function initiate (arguments) {
  return new Promise((resolve, reject) => {
    try {
      if (arguments) {
         return resolve(true);
      }
      return resolve(false);
    } catch (e) {
      reject(e);
    }
  });
}

initiate(true)
  .then(res => {
        if (res) {
          doSomethingElse();
        } else {
          doSomething();
        }
  ).catch(e => {
            logError(e.message);
            restartApp();
          }
  );複製代碼

這是一我的爲的例子,但我認爲你能get到那個點。咱們有一個方法,它返回一個Promise對象,咱們用這個對象去管理可能返回的不一樣請求。

你或許認爲這個代碼閱讀起來不太難,但我認爲它能寫的更好。

若是咱們擺脫全部匿名函數,結果會如何?

function initiate (arguments) {
  return new Promise(checkForArguments);
}
function checkForArguments (resolve, reject) {
  try {
    if (arguments) {
     return resolve(true);   
    }
    return resolve(false);
  } catch (e) {
    reject(e);
  }
}
function evaluateRes (res) {
  if (res) {
    doSomethingElse();
  } else {
    doSomething();
  }
}
function handleError (e) {
  logError(e.message);
  restartApp();
}
initiate(true)
  .then(evaluateRes)
  .catch(handleError);複製代碼

好了,讓咱們來屢清一下思路:這段代碼比以前的要長,但我認爲它比以前的可讀性高得多!咱們使用了很是棒的命名函數而不是匿名函數。只要咱們看到命名函數,它的名字就會暗示咱們接下來會發生什麼。它消除了咱們在閱讀代碼時的心理障礙。

這也有利於獨立問題。不只僅是建立方法,傳遞參數,運行邏輯,在第二個例子中,傳遞參數給thencatch,咱們能夠很清楚的知道函數中的每一處都發生了什麼。

時間很少了,不然我要讓你信服這些代碼能夠更加易讀。也許你任然不相信,那嘗試看看第三個論據...

可重用性

你有注意到最後一個例子嗎?全部函數都是能夠互相調用的!

當你使用匿名函數時,它們很難遍及你的程序。重用代碼能夠下降能耗,你也不用一遍遍寫重複的代碼了。還有你們都知道的一點,代碼越少,引入BUG的概率越小,並且用戶能夠加載更少的資源。雙贏的局面!

相反的,命名函數能夠在它的整個做用域中使用,而不須要像變量同樣四處傳遞。你寫的代碼會很天然的更具重用性,由於,你能夠重用他們!

匿名函數好嗎?

是的。我必須認可,有時它們也是最好的選擇!

const stuff = [
  { hide: true, name: 'justin' },
  { hide: false, name: 'lauren' },
  { hide: false, name: 'max' },
];
const filteredStuff = stuff.filter(s => !s.hide);複製代碼

這個匿名函數s => !s.hide小而簡潔,它不會污染其它地方,也不會用在其它地方。它會在stuff.filter的堆棧跟蹤中顯示。若是你須要重用它,最好重用整個語句:

function filterByHide (array) {
  return array.filter(item => !item.hide);
}複製代碼

有時你須要包裹你全部的代碼在一個匿名函數中,以保證全局做用域不被污染。

(() => {
 ... your code here ...
})();複製代碼

頂級匿名函數不會影響到堆棧跟蹤。代碼重用是不會形成傷害的,由於代碼重用的所有目的就是保持方法被包含。

我相信還有其餘地方適合使用匿名函數,在評論中分享一下吧!

感謝閱讀,離開這裏後,中止寫匿名函數吧!

喜歡本文的朋友能夠關注個人微信公衆號,不按期推送一些好文。

本文由Rockjins Blog翻譯,轉載請與譯者聯繫。不然將追究法律責任。

相關文章
相關標籤/搜索